<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>cmpxchg8b</title><link>https://lock.cmpxchg8b.com/#articles</link><atom:link href="http://rss.144-124-237-35.sslip.io/cmpxchg8b/articles" rel="self" type="application/rss+xml"></atom:link><description>cmpxchg8b - Powered by AtomRSS</description><generator>AtomRSS</generator><webMaster>contact@atomgroup.dev (AtomRSS)</webMaster><language>en</language><lastBuildDate>Sat, 08 Aug 2026 07:03:32 GMT</lastBuildDate><ttl>5</ttl><item><title>I miss the uMatrix Chrome extension.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Replacing uMatrix.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: f0cc50a6fc6a5dd652c2b96ca9c1779f763fd6b1 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;introduction&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;There used to be a fantastic chrome extension called &lt;a href=&quot;https://github.com/gorhill/uMatrix&quot;&gt;uMatrix&lt;/a&gt;, written by
Raymond Hill, the uBlock Origin developer. uMatrix was an intuitive way
to control site permissions and subresource requests.&lt;/p&gt;
&lt;p&gt;It looked like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/umatrix.jpg&quot; class=&quot;bare&quot; style=&quot;width:100.0%&quot; alt=&quot;uMatrix1&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;uMatrix1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/umatrix3.jpg&quot; style=&quot;width:30.0%&quot; alt=&quot;uMatrix3&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/umatrix2.jpg&quot; style=&quot;width:30.0%&quot; alt=&quot;uMatrix2&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/umatrix4.jpg&quot; style=&quot;width:30.0%&quot; alt=&quot;uMatrix3&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This isn’t something you’ll care about unless you’re a nerd – but it
let you limit what third parties could serve subresource requests, and
controlled access to features like frames, scripts, video, fonts and so
on. You can do some of that manually with browser settings, but uMatrix
made it quick and easy. This meant that what would otherwise be quite a
laborious and fiddly way to browse the web became simple. Okay, not
simple, but simpl&lt;em&gt;er&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, I enjoyed all the extra knobs to control website
permissions.&lt;/p&gt;
&lt;p&gt;The features in uMatrix were – more or less – a subset of the
features available in &lt;a href=&quot;https://github.com/gorhill/uBlock&quot;&gt;uBlock
Origin&lt;/a&gt;, so rather than maintain both, Hill deprecated uMatrix and
users were encouraged to migrate to uBO.&lt;/p&gt;
&lt;p&gt;This actually worked okay for me – uBO added some features I quite
liked (e.g.&amp;nbsp;&lt;a href=&quot;https://github.com/gorhill/ublock/wiki/Procedural-cosmetic-filters&quot;&gt;cosmetic
filters&lt;/a&gt;). and if you didn’t mind writing the more complex rules by
hand, it could basically do everything uMatrix could do.&lt;/p&gt;
&lt;p&gt;The bad news is that uBO is a &lt;a href=&quot;https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3&quot;&gt;legacy
MV2 extension&lt;/a&gt;, and the successor – &lt;a href=&quot;https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh?hl=en&quot;&gt;uBO
Lite&lt;/a&gt; – removed this feature.&lt;/p&gt;
&lt;p&gt;I really don’t want to give that up – is there a solution?&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;options&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Options&lt;/h2&gt;
&lt;p&gt;My question is could uMatrix even be possible under mv3?&lt;/p&gt;
&lt;section id=&quot;declarativenetrequest&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;declarativeNetRequest&lt;/h3&gt;
&lt;p&gt;The main difference between mv2 and mv3 here is that it is no longer
possible to block requests while you execute a callback, a so-called
“blocking” web request.&lt;/p&gt;
&lt;p&gt;If you want to intercept a request, it now has to be done &lt;a href=&quot;https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest&quot;&gt;declaratively&lt;/a&gt;.
That just means instead of running some javascript on every single
request, you have to declare what actions you want applied in
advance.&lt;/p&gt;
&lt;p&gt;Here is the controversial bit you’ve probably read about: You can’t
do &lt;em&gt;everything&lt;/em&gt; that was possible in a callback declaratively, so
the capabilities have been nerfed. That is technically true, but
practically the rules are flexible enough for everything I would ever
want.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;policies&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Policies&lt;/h3&gt;
&lt;p&gt;The web already has a system to control features and subresources,
it’s called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP&quot;&gt;Content
Security Policy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The obvious design is to use &lt;code&gt;declarativeNetRequest&lt;/code&gt; to
add our own &lt;code&gt;Content-Security-Policy&lt;/code&gt;, then the browser will
do all the work for us. All we need to do is provide a convenient
interface to manage it.&lt;/p&gt;
&lt;p&gt;Okay, but how will we offer a list of subresources to allow and deny?
When you used uMatrix, it would show you a list of subresource types and
origins, and let you manually approve or deny them.&lt;/p&gt;
&lt;p&gt;This was the core thing uMatrix did well, so it’s essential. Well, I
think CSP already provides a solution! There is a &lt;code&gt;report-to&lt;/code&gt;
directive that instructs the browser to post any security violations to
a reporting endpoint.&lt;/p&gt;
&lt;p&gt;All we have to do is enable that feature, add another
&lt;code&gt;declarativeNetRequest&lt;/code&gt; rule to catch those reports, then
populate a list from the reports the browser sent. That’s another task
successfuly offloaded 😂&lt;/p&gt;
&lt;p&gt;Of course, this will take some plumbing, but the pieces are all there
and it seems completely viable to me.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;outline&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Outline&lt;/h2&gt;
&lt;p&gt;So now that we have a rough outline for how it &lt;em&gt;could&lt;/em&gt; work…
someone just has to write a proof of concept. Ah fine, I’ll give it a
shot.&lt;/p&gt;
&lt;p&gt;Here is a first attempt, &lt;a href=&quot;https://github.com/taviso/matrix3&quot;&gt;matrix³&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/m31.png&quot; class=&quot;bare&quot; alt=&quot;matrix³&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;matrix³&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/m31.png&quot; style=&quot;width:30.0%&quot; alt=&quot;matrix³&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/m32.png&quot; style=&quot;width:30.0%&quot; alt=&quot;matrix³&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/m33.png&quot; style=&quot;width:30.0%&quot; alt=&quot;matrix³&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is clearly in a prototype state, but it basically works. I thought
I’d post about this on my blog and see if there’s any interest!&lt;/p&gt;
&lt;p&gt;The code is minimal – no building or dependencies required, I didn’t
use any frameworks. You can just &lt;code&gt;git clone&lt;/code&gt; and then “Load
Unpacked” in Chrome.&lt;/p&gt;
&lt;p&gt;I am not a web developer, and I clearly have zero UX skills 🦎 Please
let me know if this is a bad idea, you know a better solution, or you
want to help!&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/umatrix.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/umatrix.html</guid><author>Tavis Ormandy</author></item><item><title>Hey… quick question, why are anime catgirls blocking access to the Linux kernel?</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Anubis.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: f0cc50a6fc6a5dd652c2b96ca9c1779f763fd6b1 $&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;&lt;em&gt;Hey… quick question, why are anime catgirls blocking my access to
the Linux kernel?&lt;/em&gt;&lt;/p&gt;
&lt;section id=&quot;intro&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;I’ve started running into more sites recently that deploy &lt;a href=&quot;https://github.com/TecharoHQ/anubis&quot;&gt;Anubis&lt;/a&gt;, a sort of hybrid
art project slash network countermeasure. The project “weighs the souls”
of HTTP requests to help protect the web from AI crawlers.&lt;/p&gt;
&lt;p&gt;If you’ve seen anime catgirl avatars when visiting a new website,
that’s Anubis.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/anubis.png&quot; class=&quot;bare&quot; style=&quot;width:75.0%&quot; alt=&quot;A website blocked with Anubis&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;A website blocked with
Anubis&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m sympathetic to the cause – I host this blog on a single core
128MB VPS, I can tell you some stories about aggressive crawlers!&lt;/p&gt;
&lt;p&gt;Anubis recently started blocking how I access &lt;a href=&quot;https://git.kernel.org/&quot;&gt;git.kernel.org&lt;/a&gt; and &lt;a href=&quot;https://lore.kernel.org/&quot;&gt;lore.kernel.org&lt;/a&gt;. Those sites host the
&lt;a href=&quot;https://en.wikipedia.org/wiki/LKML&quot;&gt;Linux Kernel Mailing
List&lt;/a&gt; archive and the kernel git repositories. As far as I know I do
have a soul, I just wasn’t using a desktop browser… so how exactly is my
soul being weighed?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Linux has Tux 🐧, OpenBSD has Puffy 🐡, SuSE has Geeko 🦎 and
Microsoft has Bob 🤓… nothing wrong with mascots! 😸&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;problem&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;The traditional solution to blocking nuisance crawlers is to use a
combination of &lt;a href=&quot;https://en.wikipedia.org/wiki/Rate_limiting&quot;&gt;rate limiting&lt;/a&gt; and
&lt;a href=&quot;https://en.wikipedia.org/wiki/Captcha&quot;&gt;CAPTCHAs&lt;/a&gt;. The
CAPTCHA forces vistors to solve a problem designed to be very difficult
for computers but trivial for humans. This isn’t perfect of course, we
can debate the accessibility tradeoffs and weaknesses, but conceptually
the idea makes some sense.&lt;/p&gt;
&lt;p&gt;Anubis – confusingly – inverts this idea. It insists visitors solve a
problem trivial for computers, but impossible for humans. Visitors are
asked to brute force a value that when appended to a challenge string,
causes its SHA-256 to begin with a few zero nibbles.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode go&quot;&gt;&lt;code class=&quot;sourceCode go&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    calcString &lt;span class=&quot;op&quot;&gt;:=&lt;/span&gt; fmt&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Sprintf&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;%s%d&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; challenge&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; nonce&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    calculated &lt;span class=&quot;op&quot;&gt;:=&lt;/span&gt; internal&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;SHA256sum&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;calcString&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; subtle&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;ConstantTimeCompare&lt;span class=&quot;op&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;calculated&lt;span class=&quot;op&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;co&quot;&gt;// compare the leading zeroes&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!&lt;/span&gt;strings&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;HasPrefix&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; strings&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Repeat&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; rule&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Challenge&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Difficulty&lt;span class=&quot;op&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-10&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-11&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb1-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;source: &lt;a href=&quot;https://github.com/TecharoHQ/anubis/blob/e8dfff635015d6c906dddd49cb0eaf591326092a/lib/challenge/proofofwork/proofofwork.go#L66&quot;&gt;lib/challenge/proofofwork/proofofwork.go#L66&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If that sounds familiar, it’s because it’s similar to how bitcoin
mining works. Anubis is not literally mining cryptocurrency, but it
&lt;em&gt;is&lt;/em&gt; similar in concept to other projects that do exactly that,
perhaps most famously &lt;a href=&quot;https://torrentfreak.com/the-pirate-bay-website-runs-a-cryptocurrency-miner-170916/&quot;&gt;Coinhive&lt;/a&gt;
and &lt;a href=&quot;https://github.com/JSEcoin/website/blob/master/README.md&quot;&gt;JSECoin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So how do some useless SHA-256 operations prove you’re not a bot? The
argument goes that this simply makes it too expensive to crawl your
website.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/datacenter.png&quot; class=&quot;clear&quot; alt=&quot;The typical datacenter used by an AI crawler&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;The typical datacenter used by an AI
crawler&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This… makes no sense to me. Almost by definition, an AI vendor will
have a datacenter full of compute capacity. It feels like this solution
has the problem backwards, effectively only limiting access to those
&lt;em&gt;without&lt;/em&gt; resources or trying to conserve them.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;numbers&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Numbers&lt;/h2&gt;
&lt;p&gt;Let’s assume the argument has some merit and math out the claims.&lt;/p&gt;
&lt;p&gt;We can see that with the default Anubis configuration, a typical
website visitor will have to solve a challenge with a difficulty of
4.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode go&quot;&gt;&lt;code class=&quot;sourceCode go&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// DefaultDifficulty is the default &quot;difficulty&quot; (number of leading zeroes)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb2-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// that must be met by the client in order to pass the challenge.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb2-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;const&lt;/span&gt; DefaultDifficulty &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;source: &lt;a href=&quot;https://github.com/TecharoHQ/anubis/blob/main/anubis.go#L32&quot;&gt;anubis.go&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that a visitor must make the first 4 hex digits of the
challenge hash be zero, so 16 bits (4 digits, one nibble each).
Therefore, you can expect to mine a suitable &lt;code&gt;nonce&lt;/code&gt; within
2^16 SHA-256 operations.&lt;/p&gt;
&lt;p&gt;If every single github star on the anubis project represents a
website that has deployed Anubis, how much would the cloud services bill
be to mine enough tokens to crawl every single website?&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/anubisgithub.png&quot; class=&quot;bare&quot; alt=&quot;Anubis Project Stars&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Anubis Project Stars&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode go&quot;&gt;&lt;code class=&quot;sourceCode go&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb3-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// CookieDefaultExpirationTime is the amount of time before the cookie/JWT expires.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb3-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;const&lt;/span&gt; CookieDefaultExpirationTime &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt; time&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Hour&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;source: &lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.go&quot;&gt;https://github.com/TecharoHQ/anubis/blob/main/anubis.go#L32&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At the time of writing, Anubis has 11,508 github stars.&lt;/p&gt;
&lt;p&gt;The default configuration means mining one token gets you access for
7 days &lt;small&gt;(although I think this expiration check is broken, see
below)&lt;/small&gt;, so we need 11,508 * 2^16 SHA-256 operations per week,
how expensive is that?&lt;/p&gt;
&lt;p&gt;To get some numbers, I started an &lt;code&gt;e2-micro&lt;/code&gt; vm on Google
Compute Engine, and ran &lt;code&gt;openssl speed&lt;/code&gt;. This is what you get
in the &lt;a href=&quot;https://cloud.google.com/free/docs/free-cloud-features#compute&quot;&gt;free
tier&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ openssl speed sha256
Doing sha256 for 3s on 16 size blocks: 6915549 sha256&#39;s in 3.00s
Doing sha256 for 3s on 64 size blocks: 4631718 sha256&#39;s in 3.00s
Doing sha256 for 3s on 256 size blocks: 393694 sha256&#39;s in 3.21s
Doing sha256 for 3s on 1024 size blocks: 100123 sha256&#39;s in 3.00s
Doing sha256 for 3s on 8192 size blocks: 13300 sha256&#39;s in 2.98s
Doing sha256 for 3s on 16384 size blocks: 7137 sha256&#39;s in 2.99s
version: 3.0.17
built on: Tue Aug  5 07:09:41 2025 UTC
options: bn(64,64)
compiler: gcc -fPIC -pthread -m64 ...
The &#39;numbers&#39; are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
sha256           36882.93k    98809.98k    31397.40k    34175.32k    36561.61k    39107.90k&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like we can test about 2^21 every second, perhaps a bit more
if we used both SMT sibling cores. This amount of compute is simply too
cheap to even be worth billing for.&lt;/p&gt;
&lt;p&gt;So (11508 websites * 2^16 sha256 operations) / 2^21, that’s about 6
minutes to mine enough tokens for every single Anubis deployment in the
world. That means the cost of unrestricted crawler access to the
internet for a week is approximately $0.&lt;/p&gt;
&lt;p&gt;In fact, I don’t think we reach a single cent per month in compute
costs until several million sites have deployed Anubis.&lt;/p&gt;
&lt;p&gt;I’m just not convinced this math works… this is &lt;em&gt;literally&lt;/em&gt;
nothing for a souless AI vendor with a monthly cloud services budget in
the 8 figures. However, the cost for real soul-owning humans with
limited access to compute is high – the Anubis forums are full of
complaints like these:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/anubisslow.png&quot; class=&quot;bare&quot; alt=&quot;The discussion forums are full of users on limited devices complaining&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;The discussion forums are full of users
on limited devices complaining&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;alternatives&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;Anubis cites &lt;a href=&quot;https://en.wikipedia.org/wiki/Hashcash#Applications&quot;&gt;hashcash&lt;/a&gt;
as the primary inspiration for their design, an anti-spam solution from
the 90s that was never widely adopted.&lt;/p&gt;
&lt;p&gt;The idea of “weighing souls” reminded me of another anti-spam
solution from the 90s… believe it or not, there was once a company that
used poetry to block spam!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20030210085930/http://habeas.com/services/swe.htm&quot;&gt;Habeas&lt;/a&gt;
would license short haikus to companies to embed in email headers. They
would then aggressively sue anyone who reproduced their poetry &lt;a href=&quot;https://web.archive.org/web/20030207155559/http://www.habeas.com/faq/index.htm#5&quot;&gt;without
a license&lt;/a&gt;. The idea was you can safely deliver any email with their
header, because it was too legally risky to use it in spam.&lt;/p&gt;
&lt;p&gt;Here’s a &lt;a href=&quot;https://web.archive.org/web/20021220112749/http://www.habeas.com/about/haiku.htm&quot;&gt;sample
haiku&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;winter into spring &lt;br&gt; brightly anticipated &lt;br&gt; like Habeas SWE
(tm)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Was this a good idea? I don’t know, but they really did &lt;a href=&quot;https://www.theregister.com/2003/04/04/habeas_sues_haiku_abusers/&quot;&gt;sue
a few spammers&lt;/a&gt;!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;workarounds&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Workarounds&lt;/h2&gt;
&lt;p&gt;So you’re trying to read LKML, but catgirl says no… is there a
solution?&lt;/p&gt;
&lt;p&gt;My issue is I don’t want to use a desktop browser to mine the
required value, so how can I get the auth cookie?&lt;/p&gt;
&lt;p&gt;If we look at the response with &lt;code&gt;curl&lt;/code&gt;, we can see the
challenge in the HTTP headers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -I https://lore.kernel.org/
HTTP/2 200
server: nginx
set-cookie: techaro.lol-anubis-auth=; Path=/
set-cookie: techaro.lol-anubis-cookie-test-if-you-block-this-anubis-wont-work=5d737f0600ff2dd; Path=/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;techaro.lol-anubis-cookie&lt;/code&gt; is the challenge, here is
a quick C program to mine an acceptable token:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb6&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb6-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#include &lt;/span&gt;&lt;span class=&quot;im&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#include &lt;/span&gt;&lt;span class=&quot;im&quot;&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#include &lt;/span&gt;&lt;span class=&quot;im&quot;&gt;&amp;lt;openssl/sha.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#pragma GCC diagnostic ignored &quot;-Wdeprecated-declarations&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; main&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; argc&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;**&lt;/span&gt;argv&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;char&lt;/span&gt; hash&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;SHA256_DIGEST_LENGTH&lt;span class=&quot;op&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; difficulty &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-10&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt;message&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-11&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    SHA256_CTX base&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-12&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-13&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-13&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;argc &lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-14&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-14&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        fprintf&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;stderr&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;usage: &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;st&quot;&gt; challenge&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt;argv&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-15&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-15&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-16&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-16&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-17&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-17&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-18&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-18&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    message &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; argv&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-19&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-19&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    SHA256_Init&lt;span class=&quot;op&quot;&gt;(&amp;amp;&lt;/span&gt;base&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-20&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-20&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    SHA256_Update&lt;span class=&quot;op&quot;&gt;(&amp;amp;&lt;/span&gt;base&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; strlen&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;op&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-21&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-21&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-22&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-22&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;op&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-23&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-23&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;dt&quot;&gt;char&lt;/span&gt; nonce&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-24&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-24&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-25&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-25&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        SHA256_CTX ctx &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; base&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-26&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-26&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        SHA256_Update&lt;span class=&quot;op&quot;&gt;(&amp;amp;&lt;/span&gt;ctx&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; nonce&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; snprintf&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;nonce&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;nonce&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;op&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-27&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-27&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        SHA256_Final&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;hash&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;ctx&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-28&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-28&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-29&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-29&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; j &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt; j &lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt; SHA256_DIGEST_LENGTH&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;op&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-30&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-30&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;hash&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;j&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-31&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-31&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-32&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-32&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;j &lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;difficulty &lt;span class=&quot;op&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-33&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-33&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;cf&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-34&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-34&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-35&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-35&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;co&quot;&gt;// Looks good&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-36&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-36&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            puts&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;nonce&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-37&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-37&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-38&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-38&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-39&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-39&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-40&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-40&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-41&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb6-41&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s run that and see what it says…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gcc -Ofast -march=native anubis-miner.c -lcrypto -o anubis-miner
$ time ./anubis-miner 5d737f0600ff2dd
47224

real    0m0.017s
user    0m0.016s
sys     0m0.000s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks okay, let’s verify that solution is correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ printf &quot;5d737f0600ff2dd%d&quot; 47224 | sha256sum
000043f7c4392a781a04419a7cb503089ebcf3164e2b1d4258b3e6c15b8b07f1  -&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It seems valid, so now we can get a signed auth cookie by sending
back the value we mined:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -I --cookie &quot;techaro.lol-anubis-cookie-test-if-you-block-this-anubis-wont-work=5d737f0600ff2dd&quot; \
    &#39;https://lore.kernel.org/.within.website/x/cmd/anubis/api/pass-challenge?response=000043f7c4392a781a04419a7
cb503089ebcf3164e2b1d4258b3e6c15b8b07f1&amp;amp;nonce=47224&amp;amp;redir=/&amp;amp;elapsedTime=0&#39;
HTTP/2 302
server: nginx
location: /
set-cookie: techaro.lol-anubis-auth=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJhY3Rpb24iO...OTYifQ...;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Success, this cookie is now valid for 1 week of access. Let’s
validate what it sent us, the actual schema is visible in the code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb10&quot;&gt;&lt;pre class=&quot;sourceCode go&quot;&gt;&lt;code class=&quot;sourceCode go&quot;&gt;&lt;span id=&quot;cb10-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb10-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    claims&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Now&lt;span class=&quot;op&quot;&gt;().&lt;/span&gt;Unix&lt;span class=&quot;op&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb10-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    claims&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;nbf&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Now&lt;span class=&quot;op&quot;&gt;().&lt;/span&gt;Add&lt;span class=&quot;op&quot;&gt;(-&lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt; time&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Minute&lt;span class=&quot;op&quot;&gt;).&lt;/span&gt;Unix&lt;span class=&quot;op&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb10-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/anubis.html#cb10-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    claims&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;Now&lt;span class=&quot;op&quot;&gt;().&lt;/span&gt;Add&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;opts&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;CookieExpiration&lt;span class=&quot;op&quot;&gt;).&lt;/span&gt;Unix&lt;span class=&quot;op&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;source: &lt;a href=&quot;https://github.com/TecharoHQ/anubis/blob/e8dfff635015d6c906dddd49cb0eaf591326092a/lib/http.go#L312&quot;&gt;lib/http.go&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can examine this auth token and see what Anubis gave us…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ base64 -d &amp;lt;&amp;lt;&amp;lt; eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9 | jq
{
  &quot;alg&quot;: &quot;EdDSA&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}
$ base64 -d &amp;lt;&amp;lt;&amp;lt; eyJhY3Rpb24iO...OTYifQ== | jq
{
  &quot;action&quot;: &quot;CHALLENGE&quot;,
  &quot;challenge&quot;: &quot;5d737f0600ff2dd&quot;,
  &quot;exp&quot;: 1756185722,
  &quot;iat&quot;: 1755580922,
  &quot;method&quot;: &quot;fast&quot;,
  &quot;nbf&quot;: 1755580862,
  &quot;policyRule&quot;: &quot;dbf942088788cc96&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like &lt;code&gt;exp&lt;/code&gt; is the expiry date, so
&lt;code&gt;1756185722&lt;/code&gt;, which is…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ date --date @1756185722
Mon Aug 25 22:22:02 PDT 2025&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yep, about 7 days from the date I requested it. You can now place
that into a cookie file for &lt;code&gt;curl&lt;/code&gt;,&lt;code&gt;lynx&lt;/code&gt; etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Interestingly, sending the same request the next day got me a new
signed cookie!?&lt;/p&gt;
&lt;p&gt;This seems like a bug – exchanging a mined token for an auth cookie
should immediately remove the challenge from the store, or there is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Double-spending&quot;&gt;double spend&lt;/a&gt;
vulnerability.&lt;/p&gt;
&lt;p&gt;This error benefits me, I have to mine less tokens, but I’ll open an
issue 😇&lt;/p&gt;
&lt;p&gt;Update: wow, &lt;a href=&quot;https://github.com/TecharoHQ/anubis/pull/1003&quot;&gt;fixed&lt;/a&gt; just a
few minutes after opening an issue by the maintainer!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This dance to get access is just a minor annoyance for me, but I
question how it proves I’m not a bot. These steps can be trivially and
cheaply automated.&lt;/p&gt;
&lt;p&gt;I think the end result is just an internet resource I need is a
little harder to access, and we have to waste a small amount of
energy.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;notes&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I wrote this article with my own puny brain, I didn’t use any AI. I
know there are &lt;em&gt;endashes&lt;/em&gt; (they’re &lt;strong&gt;not&lt;/strong&gt;
emdashes!) – I have a habit of writing two consecutive dashes, which &lt;a href=&quot;https://pandoc.org/demo/example33/7.1-typography.html&quot;&gt;pandoc&lt;/a&gt;
converts into &lt;a href=&quot;https://unicode-explorer.com/c/2013&quot;&gt;U+2013&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;This post is a bit critical of a small well-intentioned project, so
I felt obliged to email the maintainer to discuss it before posting it
online. I didn’t hear back.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/anubis.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/anubis.html</guid><author>Tavis Ormandy</author></item><item><title>Troubleshooting a slow terminal startup issue.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Troubleshooting: Terminal Lag&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;I think I should blog more about random troubleshooting sessions, if
nothing else it will remind me what steps I took when it inevitably
happens again!&lt;/p&gt;
&lt;p&gt;Okay, here is the first one – why is my &lt;code&gt;xterm&lt;/code&gt; opening so
slowly?&lt;/p&gt;
&lt;section id=&quot;background&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I have two similarly specced machines at my desk – my primary
workstation running Fedora Linux, and a Windows 11 machine. They share
the same monitor and input devices, and I switch between them with an
iogear KVM.&lt;/p&gt;
&lt;p&gt;I do the bulk of my work in either a browser or a terminal. This is
true even on Windows, where I rely heavily on
&lt;abbr title=&quot;Windows Subsystem for Linux&quot;&gt; WSL&lt;/abbr&gt;.&lt;/p&gt;
&lt;p&gt;This works well for me, and I’m happy enough with the setup.&lt;/p&gt;
&lt;section id=&quot;issue&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Issue&lt;/h4&gt;
&lt;p&gt;I have the shortcut
&lt;kbd&gt;&lt;abbr title=&quot;Windows Key&quot;&gt;Super&lt;/abbr&gt;&lt;/kbd&gt;+&lt;kbd&gt;1&lt;/kbd&gt; bound to
&lt;code&gt;xterm&lt;/code&gt; on both machines, and I probably use this hundreds of
times per day.&lt;/p&gt;
&lt;p&gt;Here is how that looks on Fedora:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/fedora_terminal_open.gif&quot; class=&quot;bare&quot; style=&quot;width:100.0%&quot; alt=&quot;Fedora Terminal&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Fedora Terminal&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;It takes about &lt;abbr title=&quot;9 frames @ 30fps&quot;&gt;300ms&lt;/abbr&gt; from key
activation to a terminal being ready. This is fine, I’ve never noticed
any problem.&lt;/p&gt;
&lt;p&gt;However, let’s compare that to Windows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/windows_terminal_open.gif&quot; class=&quot;bare&quot; style=&quot;width:100.0%&quot; alt=&quot;Windows Terminal&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Windows Terminal&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s about &lt;abbr title=&quot;48 frames @ 30fps&quot;&gt;1600ms&lt;/abbr&gt; before I
can type, over 5 times slower! This &lt;em&gt;is&lt;/em&gt; slow enough that it
bothers me, and I use this shortcut so often that I want to solve
it.&lt;/p&gt;
&lt;p&gt;I don’t think many people care about &lt;code&gt;xterm&lt;/code&gt; performance
on Windows, so I guess that means it’s up to me to solve this 🙂&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;window-effects&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Window Effects&lt;/h2&gt;
&lt;p&gt;Hey, wait a minute… &lt;big&gt;&lt;em&gt;&lt;strong&gt;ENHANCE!&lt;/strong&gt;&lt;/em&gt;&lt;/big&gt;
👀&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/windows_terminal_fade.gif&quot; class=&quot;bare&quot; style=&quot;width:100.0%&quot; alt=&quot;Windows Fading Effect&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Windows Fading Effect&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why does the window fade in like that? It looks like the Window is
ready when the effect starts, but I can’t interact with it until it
completes. If I count those frames, this animation must be costing me
~200ms! 🤬&lt;/p&gt;
&lt;p&gt;I always disable anything like animation or compositing effects, so
I’m confused where this is coming from.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/windows_performance.png&quot; class=&quot;bare&quot; alt=&quot;Windows Performance Settings&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Windows Performance Settings&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;I tested with some native windows programs like &lt;code&gt;notepad&lt;/code&gt;
and &lt;code&gt;calc&lt;/code&gt; – they just appear instantly… so what is causing
that?&lt;/p&gt;
&lt;p&gt;I experimented with it a bit, I can see other windows behind it as it
fades in, so I think something must be changing the opacity. Searching
msdn, it looks like the function for that is &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setlayeredwindowattributes&quot;&gt;&lt;code&gt;SetLayeredWindowAttributes()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Could something be calling that, is my X server betraying me?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dumpbin /imports X410.exe  | grep SetLayeredWindowAttributes
                         33A SetLayeredWindowAttributes&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks like the culprit!! I’m using a server called &lt;a href=&quot;https://x410.dev/&quot;&gt;X410&lt;/a&gt;, it seems like it’s adding it’s own
animation effects, and doesn’t have any way to disable it. I’m reluctant
to switch to an alternative – that could just replace this issue with a
different issue to troubleshoot.&lt;/p&gt;
&lt;p&gt;Is it possible I can just stop it from doing that with a
debugger?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cdb -p 6624
(19e0.2ad0): Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00007ff9`1f9b3c90 cc              int     3
0:014&amp;gt; eb win32u!NtUserSetLayeredWindowAttributes c3
0:014&amp;gt; .detach
Detached
NoTarget&amp;gt; q
quit:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ah-ha, that actually worked!!!&lt;/p&gt;
&lt;p&gt;I’ve added that &lt;code&gt;cdb&lt;/code&gt; command into my xinit
initialization, and it looks a lot snappier. That saved nearly 300ms, so
we’re down to just 4 times slower than Fedora!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;profiling&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Profiling&lt;/h1&gt;
&lt;p&gt;Okay, let’s try get some real numbers. I like the tool &lt;a href=&quot;https://github.com/sharkdp/hyperfine&quot;&gt;hyperfine&lt;/a&gt; for this, here
are the initial results:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hyperfine_windows.png&quot; class=&quot;bare&quot; alt=&quot;Hyperfine Windows&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Hyperfine Windows&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we run it under optimal conditions, it takes about 900ms on
Windows, and about 100ms on Fedora.&lt;/p&gt;
&lt;p&gt;Now that I can reproduce the delay reliably, I can start exploring
some theories…&lt;/p&gt;
&lt;section id=&quot;filesystem&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Filesystem&lt;/h4&gt;
&lt;p&gt;My first thought is that filesystem performance under WSL can be very
slow, could that explain the difference?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;taviso@WORKSTATION:~$ strace -wc -efile xterm -e true
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 43.93    0.014870          56       261        56 openat
 23.79    0.008053          31       257        33 access
 20.48    0.006932          32       211        12 newfstatat
  7.07    0.002394          23       100        54 readlink
  4.72    0.001596        1596         1           execve
------ ----------- ----------- --------- --------- ----------------
100.00    0.033845          40       830       155 total&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nope, it’s actually a bit faster on Windows! If I browse the logs, it
looks related to fonts, and I do have fewer fonts installed on Windows.
I suspect that causes &lt;code&gt;fontconfig&lt;/code&gt; to query less files on
initialization.&lt;/p&gt;
&lt;p&gt;Whatever the reason, I concluded it wasn’t a big proportion of
startup time, so it doesn’t seem worth worrying about.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;x-server&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;X Server&lt;/h4&gt;
&lt;p&gt;The issue must be the X server, how fast is a very simple X
client?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;taviso@fedora:~$ hyperfine xdpyinfo
Benchmark 1: xdpyinfo
  Time (mean ± σ):       4.6 ms ±   0.8 ms    [User: 1.9 ms, System: 1.6 ms]
  Range (min … max):     3.1 ms …   9.4 ms    317 runs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;em&gt;does&lt;/em&gt; run slower on Windows, but not significantly
slower – perhaps I actually need to create a window to see a
difference?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;taviso@fedora:~$ x11perf -repeat 1 -subs 8 -popup
    5600000 reps @   0.0010 msec (1040000.0/sec): Hide/expose window via popup (8 kids)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is also slower on Windows – this is understandable, it has to
translate from X11 to win32 – but not so slow that it adequately
explains the problem alone.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;freetype&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;FreeType&lt;/h4&gt;
&lt;p&gt;Could it be a FreeType or FontConfig issue?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;taviso@WORKSTATION:~$ ftbench -p consola.ttf
executing tests:
  Load                           2.491 us/op     809010 done
  Load_Advances (Normal)         2.437 us/op     827190 done
  Load_Advances (Fast)           0.022 us/op   88575990 done
  Load_Advances (Unscaled)       0.013 us/op  147448890 done
  Render                         2.039 us/op     390870 done
  Get_Glyph                      0.921 us/op     509040 done
  Get_Char_Index                 0.018 us/op  111551968 done
  Iterate CMap                  21.539 us/op      88503 done
  New_Face                       6.271 us/op     284029 done
  Embolden                       2.491 us/op     357540 done
  Stroke                        24.663 us/op      69690 done
  Get_BBox                       0.865 us/op     487830 done
  Get_CBox                       0.679 us/op     506010 done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Loading fonts is slightly slower, but the other numbers seem fine,
and it’s not &lt;em&gt;that&lt;/em&gt; much slower. I don’t think it’s this.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;features&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Features&lt;/h3&gt;
&lt;p&gt;Maybe I can get some clues from ltrace?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ltrace -c xterm -e true
% time     seconds  usecs/call     calls      function
------ ----------- ----------- --------- --------------------
 26.74    1.977389      988694         2 XtRealizeWidget
 22.18    1.640132       14139       116 XtSetValues
 13.39    0.989966      197993         5 XtVaCreateManagedWidget
  6.42    0.474410         200      2361 strlen
  5.51    0.407617      203808         2 read
  4.21    0.311086         197      1572 FcCharSetHasChar
  2.92    0.215975        2571        84 XtCreateManagedWidget
  2.80    0.207397       69132         3 XtVaCreatePopupShell
  2.39    0.176458         218       808 XftTextExtents32
  1.51    0.111306      111306         1 XpmReadFileToPixmap
  1.48    0.109486      109486         1 XtOpenApplication
  1.44    0.106786         203       524 malloc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;XtRealizeWidget&lt;/code&gt; call does seem slow, and I don’t
see that on Fedora, what is calling that?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gdb --args ./xterm -e true
Reading symbols from ./xterm...
(gdb) b XtRealizeWidget
Breakpoint 1 at 0x2db20
(gdb) r
Breakpoint 1, 0x00007fffff43d940 in XtRealizeWidget () from /lib/x86_64-linux-gnu/libXt.so.6
(gdb) bt
#0  0x00007fffff43d940 in XtRealizeWidget () from /lib/x86_64-linux-gnu/libXt.so.6
...
#4  0x00007fffff44c4f6 in XtSetValues () from /lib/x86_64-linux-gnu/libXt.so.6
#5  0x0000000008086683 in UpdateMenuItem (menu=0x811c5c0 &amp;lt;mainMenuEntries&amp;gt;, which=0, val=1)
    at ./menu.c:1026
#6  0x000000000808a763 in update_toolbar () at ./menu.c:3366&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ah-ha – it’s the toolbar feature. It’s disabled at compile time on
Fedora, but I quite like it and enable it on Windows.&lt;/p&gt;
&lt;p&gt;If I disable that, startup is a little faster, I wonder if there are
any other features that are slowing down initialization…?&lt;/p&gt;
&lt;section id=&quot;parameter-scanning&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Parameter Scanning&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;hyperfine&lt;/code&gt; utility has a feature called &lt;em&gt;parameter
scan&lt;/em&gt;, where it it will try a bunch of settings for you and tell you
which one is fastest.&lt;/p&gt;
&lt;p&gt;Let’s ask XTerm what features are available, and toggle each one on
and off.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xterm -report-xres -e true
activeIcon              : default
allowBoldFonts          : true
allowC1Printable        : false
allowColorOps           : true
allowFontOps            : false
allowMouseOps           : true
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ll start by extracting all the settings that are booleans.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xterm -report-xres -e true | grep -Po &#39;^\S+(?=\s+: (true|false))&#39; | tr &#39;\n&#39; &#39;,&#39;
allowBoldFonts,allowColorOps,allowMouseOps...&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;xterm&lt;/code&gt; has a &lt;em&gt;lot&lt;/em&gt; of features, I’m
truncating the list for brevity!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we can give each of those to &lt;code&gt;hyperfine&lt;/code&gt;, and let it
figure out which settings have the most noticable effect.&lt;/p&gt;
&lt;p&gt;That took about 20 minutes to run, and reports:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hyperfine --parameter-list res allowBoldFonts,allow... \
            --parameter-list bool true,false \
            &quot;xterm -xrm &#39;XTerm*{res}: {bool}&#39; -e true&quot;
...
Benchmark 240: xterm -xrm &#39;XTerm*xftTrackMemUsage: false&#39; -e true
  Time (mean ± σ):     140.1 ms ±   7.4 ms    [User: 30.8 ms, System: 23.4 ms]
  Range (min … max):   129.2 ms … 153.4 ms    21 runs
 
Summary
  xterm -xrm &#39;XTerm*tekInhibit: true&#39; -e true ran
    1.01 ± 0.08 times faster than xterm -xrm &#39;XTerm*allowSendEvents: false&#39; -e true
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This helped a little, I found a combination of options that saved
around 200ms total. One example was &lt;code&gt;tekInhibit&lt;/code&gt;, which
disables the Tektronix emulation. That’s usually used as a graphing mode
– it’s actually &lt;a href=&quot;https://web.archive.org/web/20100305175059/http://dim13.org/tek/&quot;&gt;pretty
cool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Still, it isn’t a big enough difference, and this is as far as I was
able to get through tweaking settings.&lt;/p&gt;
&lt;p&gt;I’m starting to think that this is just death by a thousand cuts,
everything just has some small overhead on Windows and it adds up…&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;caching&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Caching&lt;/h1&gt;
&lt;p&gt;There’s a simple generic solution to slow startup performance: server
mode.&lt;/p&gt;
&lt;p&gt;The idea is to cache a few processes in the background, then all the
slow stuff will already be done, ready for you to start working
immediately.&lt;/p&gt;
&lt;p&gt;XTerm doesn’t have this feature natively, but it’s not complicated, I
can add it.&lt;/p&gt;
&lt;p&gt;To do this, I will use &lt;em&gt;deferred mapping&lt;/em&gt; – that just means
that a program is running, but the window is not visible yet.&lt;/p&gt;
&lt;p&gt;I tried a few solutions and found one that works well, an
&lt;code&gt;LD_PRELOAD&lt;/code&gt; library. All it does is intercept any toplevel
&lt;a href=&quot;https://linux.die.net/man/3/xmapwindow&quot;&gt;&lt;code&gt;XMapWindow()&lt;/code&gt;&lt;/a&gt;
calls, then pause execution until it receives a signal.&lt;/p&gt;
&lt;p&gt;It’s a bit hacky, but my code is &lt;a href=&quot;https://github.com/taviso/defermap&quot;&gt;here&lt;/a&gt;, if you’re
interested.&lt;/p&gt;
&lt;p&gt;To use it, you need something to manage the cache for you in the
background, &lt;code&gt;xargs&lt;/code&gt; will work!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xargs --null --arg-file=/dev/zero --max-procs=3 --replace -- \
     env LD_PRELOAD=defermap.so xterm -display :0 [PARAMS...]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will keep three xterms running in the background.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you often rapidly start terminals in quick succession,
increase &lt;code&gt;max-procs&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you want a new terminal, instead of running &lt;code&gt;xterm&lt;/code&gt;
as you normally would, do this instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pkill --oldest --signal SIGUSR1 xtermserver&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A terminal should appear near-instantly. You can now execute that
instead of &lt;code&gt;xterm&lt;/code&gt;, and startup performance should be
solved.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This whole process took a while! Now I need to adjust my shortcuts to
run &lt;code&gt;pkill&lt;/code&gt; instead of &lt;code&gt;xterm&lt;/code&gt;, and I can compare
the results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/windows_terminal_fixed.gif&quot; class=&quot;bare&quot; style=&quot;width:100.0%&quot; alt=&quot;Windows Terminal&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Windows Terminal&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;Counting the frames in that video, it’s down to about
&lt;abbr title=&quot;11 frames @
30fps&quot;&gt;366ms&lt;/abbr&gt;, just 60ms slower than Fedora, this is totally
acceptable!&lt;/p&gt;
&lt;p&gt;I’ve been using this configuration for a few days, so far it’s
working great. I haven’t noticed any issues running it this way.&lt;/p&gt;
&lt;p&gt;I highly doubt anyone else will find this useful, who else is using
XTerm on Windows? 😆&lt;/p&gt;
&lt;p&gt;Nevertheless, if you have a better solution, or can think of
something else I can try, let me know!&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/slowterm.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/slowterm.html</guid><author>Tavis Ormandy</author></item><item><title>I wrote a brief introduction to the hiew hex editor.I started using ledger to track my finances.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Hiew Hex Editor&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;introduction&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;A big part of my daily work is digging around in binary blobs. I’ve
been using Hiew almost exclusively for a few years, and I’m a big fan –
more people should use it!&lt;/p&gt;
&lt;p&gt;The problem is Hiew has a steep learning curve, so if you literally
use a hex editor once a year, then it’s probably just not worth it.
However, if you live in a hex editor, then it might be more interesting
than you realized!&lt;/p&gt;
&lt;p&gt;This article assumes you’re already familiar with another hex editor,
and are curious if it’s worth your time learning Hiew.&lt;/p&gt;
&lt;p&gt;Let me walk you through a few common hex editing tasks and show you
how they look… maybe 0.01% of the people reading this will be intrigued
😂&lt;/p&gt;
&lt;section id=&quot;background&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Let’s get this out of the way - if you’ve never seen Hiew before,
it’s a console hex editor!&lt;/p&gt;
&lt;p&gt;It has basic mouse support, but you primarily operate it with the
keyboard. If you think &lt;code&gt;vim&lt;/code&gt; looks arcane, you’re not going
to enjoy using Hiew – that’s okay!&lt;/p&gt;
&lt;p&gt;Hiew has been around a long time, first released in 1991. It’s still
actively maintained – version 8.80 was just released in November – but
it comes from an era where powerful software was usually designed around
the idea of keyboard macros.&lt;/p&gt;
&lt;p&gt;Like other tools built this way, Hiew has an emphasis on chaining
together simple commands to perform complex tasks. That means there’s a
steep learning curve before you feel proficient, but as you build up
your command vocabulary you’ll be able to perform operations that look
like magic.&lt;/p&gt;
&lt;p&gt;Okay, Let’s dive right in!&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
&lt;p style=&quot;font-family: cursive; color: coral; font-size: small&quot;&gt;
&lt;em&gt;← Psst…this symbol means there’s a figure available… just click
it!&lt;/em&gt;
&lt;/p&gt;
&lt;/summary&gt;
&lt;span style=&quot;font-size: larger&quot;&gt; 🎈 &lt;/span&gt;
&lt;/details&gt;
&lt;/section&gt;
&lt;section id=&quot;scenarios&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Scenarios&lt;/h2&gt;
&lt;section id=&quot;scenario-1&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Scenario #1&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;You’re trying to understand an undocumented proprietary file
format.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;
A good place to start is usually looking at the list of strings that
appear in the file, that’s &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;f6&lt;/kbd&gt;.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/stringlist.png&quot; class=&quot;bare&quot; alt=&quot;String List&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;String List&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Hopefully that will give you some clues about what fields might be in
the header. In Hiew, you mark fields using &lt;em&gt;blocks&lt;/em&gt;, and you
start a block with &lt;kbd&gt;*&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;I see there’s what looks like a filename near the top, so let me mark
that.&lt;/p&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/blocksimple.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;p&gt;All the usual movement commands work while you’re defining a block,
including searching and jumping. Vim users will recognize this as very
similar to visual mode.&lt;/p&gt;
&lt;p&gt;Now that it’s marked, you can assign it a color with
&lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;m&lt;/kbd&gt;, or just give it a random color with
&lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;m&lt;/kbd&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
I like to just keep hitting &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;m&lt;/kbd&gt;
until I get a color I like.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/randomizecolor.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Okay, now you can watch me repeat that process for a few more
fields…&lt;/p&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/assignfields.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Feeling
&lt;kbd&gt;s&lt;/kbd&gt;&lt;kbd&gt;h&lt;/kbd&gt;&lt;kbd&gt;o&lt;/kbd&gt;&lt;kbd&gt;r&lt;/kbd&gt;&lt;kbd&gt;t&lt;/kbd&gt;&lt;kbd&gt;c&lt;/kbd&gt;&lt;kbd&gt;u&lt;/kbd&gt;&lt;kbd&gt;t&lt;/kbd&gt;
overload? Don’t worry, I made &lt;a href=&quot;https://taviso.github.io/hiewdocs/index.htm&quot;&gt;hiewdocs&lt;/a&gt;, an easy
online shortcut reference!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;
Now we probably want to add some notes to remind us what we think a
field is. In Hiew, you enter a comment with &lt;kbd&gt;;&lt;/kbd&gt;.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/entercomment.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Just like with a dissasembler, each location can have a &lt;em&gt;name&lt;/em&gt;
and a &lt;em&gt;comment&lt;/em&gt;, you enter a name with
&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;f12&lt;/kbd&gt;. They’re both optional, skip one or both
if you like!&lt;/p&gt;
&lt;p&gt;Once some fields are marked, you can move between them with
&lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;n&lt;/kbd&gt; (forward) and
&lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;n&lt;/kbd&gt; (backward).&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
Hiew will display any names and comments as you navigate around as
floating tips. You can also open a list with &lt;kbd&gt;f12&lt;/kbd&gt; and jump
straight to the one you’re interested in!
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/displaycomments.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;I think we’re making progress. Look at this field, I think it’s the
length of the data block:&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/compressedsize.png&quot; class=&quot;bare&quot; alt=&quot;Calculator&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Calculator&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;If I examine it in the calculator, as a &lt;code&gt;DWORD&lt;/code&gt; that would
be &lt;code&gt;203008&lt;/code&gt;. Okay, Let’s extract a block of that length and
save it to a file.&lt;/p&gt;
&lt;p&gt;So we need to…&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Hit &lt;kbd&gt;*&lt;/kbd&gt; to select a block.&lt;/li&gt;
&lt;li&gt;Press &lt;kbd&gt;f5&lt;/kbd&gt; to jump to an offset.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;+203008t&lt;/code&gt; to move forward that many bytes.&lt;/li&gt;
&lt;/ol&gt;
&lt;details&gt;
&lt;summary&gt;
Now we can use &lt;kbd&gt;f2&lt;/kbd&gt; to save the current block to a file.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/writeblock.png&quot; class=&quot;bare&quot; alt=&quot;Write Block&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Write Block&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;You can hit &lt;kbd&gt;backspace&lt;/kbd&gt; to jump back to your last location
when done.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Hiew has about a million ways to jump around a file! Don’t
worry, you don’t need to memorize them!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;
Let’s take a look at this block we saved. I think I know what this is,
if I add a deflate header…
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/zpipe.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ah-ha, we got back the uncompressed data!&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;You probably spotted the &lt;code&gt;PK&lt;/code&gt; header, and realized this is
not an undocumented file format at all, it’s a ZIP archive. Yep, you got
me 🙂&lt;/p&gt;
&lt;p&gt;You don’t need to go through this process for common formats like
Zip, you can just use a standard template that will populate the fields
for you. If I do that, the names list &lt;kbd&gt;f12&lt;/kbd&gt; looks like
this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/kiewtai.png&quot; class=&quot;bare&quot; alt=&quot;Kiewtai&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Kiewtai&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;scenario-2&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Scenario #2&lt;/h3&gt;
&lt;p&gt;You’re trying to patch an executable you don’t have the source code
for.&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;
The first thing you need to know is that Hiew has three main views. They
are &lt;em&gt;text&lt;/em&gt;, &lt;em&gt;code&lt;/em&gt; and &lt;em&gt;data&lt;/em&gt;. You can quickly flip
between them without losing your place, just hit &lt;kbd&gt;enter&lt;/kbd&gt;. Most
Hiew users get into the habit of doing this frequently!
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/hiewviews.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Let’s take a closer look at the code view.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/codeview.png&quot; class=&quot;bare&quot; alt=&quot;Code View&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Code View&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;As you can see, Hiew has a builtin disassembler, very handy if you
work in security and need to extract shellcode!&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
&lt;p&gt;Do you see those little arrow annotations? Those are shortcuts to
follow a branch, you just hit the corresponding key to jump to the
branch target. This lets you browse the code effectively without leaving
Hiew.&lt;/p&gt;
If you see &lt;code&gt;↓5&lt;/code&gt;, that means it’s a forward branch and you can
press &lt;kbd&gt;5&lt;/kbd&gt; to jump to the destination.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/branchannot.png&quot; class=&quot;bare&quot; alt=&quot;Branch Annotations&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Branch Annotations&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
While we’re browsing around we can enter comments with &lt;kbd&gt;;&lt;/kbd&gt;,
just like in data view. The only difference is that your comments get
added to the disassembly, rather than as floating tooltips!
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/codecomment.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Here’s a neat feature, see these dashes?&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/bookmarkstack.png&quot; class=&quot;bare&quot; alt=&quot;Bookmark Stack&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Bookmark Stack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s your bookmark stack! If you want to go explore somewhere else,
you can hit &lt;kbd&gt;+&lt;/kbd&gt;, then you return later by hitting
&lt;kbd&gt;-&lt;/kbd&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Take a look at &lt;a href=&quot;https://taviso.github.io/hiewdocs/index.htm?query=bookmark&quot;&gt;hiewdocs&lt;/a&gt;
if you want to master the bookmark stack!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;
&lt;p&gt;Hiew understands most major executable formats, and will populate
names automatically. You can easily browse and patch headers, sections,
tables, and so on.&lt;/p&gt;
Let’s take a look at the imports table.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/imports.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;Look, there’s a call to &lt;code&gt;IsDebuggerPresent()&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/debugger.png&quot; class=&quot;bare&quot; alt=&quot;IsDebuggerPresent&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;IsDebuggerPresent&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s patch it out just for fun. Hiew not only has a dissasembler
built in, it also has an &lt;em&gt;assembler&lt;/em&gt;. I think this is a unique
feature among major hex editors!&lt;/p&gt;
&lt;p&gt;There’s a handy shortcut to just &lt;code&gt;nop&lt;/code&gt; an instruction
&lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;f2&lt;/kbd&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/nopnop.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now let’s change this nop into &lt;code&gt;xor eax, eax&lt;/code&gt;, and we’re
done!&lt;/p&gt;
&lt;p&gt;I challenge you to find me a hex editor that makes patching
executables easier than Hiew 😂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;scenario-3&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Scenario #3&lt;/h3&gt;
&lt;p&gt;You’re trying to repair a damaged file.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Want to see a picture of my pup 🐶? His name is Deputy Dexter!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ display dexter.png
display-im6.q16: improper image header `dexter.png&#39; @ error/png.c/ReadPNGImage/4107.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Huh, that didn’t work! Maybe we can fix this problem with Hiew.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/dexbroken.png&quot; class=&quot;bare&quot; alt=&quot;Broken Header&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Broken Header&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;Okay, this doesn’t look right, there’s an extra &lt;code&gt;0D&lt;/code&gt; in
the header.&lt;/p&gt;
&lt;p&gt;I’d bet this was probably broken by someone trying to convert from
UNIX to DOS line endings!&lt;a href=&quot;https://lock.cmpxchg8b.com/hiew.html#fn1&quot; class=&quot;footnote-ref&quot; id=&quot;fnref1&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; We need to go through the file and
replace every occurrence of &lt;code&gt;0D 0A&lt;/code&gt; with &lt;code&gt;0A&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A repetitive task like this is a good opportunity for me to show you
how keyboard macros work. All we need to do is fix one of these errors,
then we can tell Hiew to repeat everything we just did.&lt;/p&gt;
&lt;p&gt;So, hit &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;.&lt;/kbd&gt; to begin recording a macro. This
recording symbol should appear.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/macroindicator.png&quot; class=&quot;bare&quot; alt=&quot;Macro Indicator&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Macro Indicator&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;
&lt;p&gt;Now we need to search for the next occurrence of &lt;code&gt;0D 0A&lt;/code&gt;,
so &lt;kbd&gt;f7&lt;/kbd&gt;, and enter &lt;code&gt;0D0A&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To delete a byte, you mark it then press
&lt;kbd&gt;shift&lt;/kbd&gt;+&lt;kbd&gt;f2&lt;/kbd&gt;.&lt;/p&gt;
That’s it! Now hit &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;.&lt;/kbd&gt; to end the macro.
&lt;/summary&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/macrorecord.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;/details&gt;
&lt;p&gt;To tell Hiew to repeat this for every occurrence in the file, open
the macro manager with &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;=&lt;/kbd&gt;, and then make sure
the &lt;code&gt;L&lt;/code&gt; (loop) and &lt;code&gt;S&lt;/code&gt; (&lt;strong&gt;s&lt;/strong&gt;earch
failure) flags are set. This just means that Hiew should repeat exection
until a search failure occurs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/macromanager.png&quot; class=&quot;bare&quot; alt=&quot;Macro Manager&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Macro Manager&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can give it a name if you want to save it for future use, but
we’re only going to use it once. So hit &lt;kbd&gt;enter&lt;/kbd&gt; to run it!&lt;/p&gt;
&lt;blockquote&gt;
&lt;video class=&quot;bare&quot; controls=&quot;&quot;&gt;
&lt;source src=&quot;https://lock.cmpxchg8b.com/img/hiew/macroplay.webm&quot; type=&quot;video/webm&quot;&gt;
&lt;/video&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: The default execution speed is 200ms, which is slow enough that
you can watch it run. It’s kinda cool to watch, but you can turn it all
the way down to 0ms if you like!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s check if that worked, can we see Dexter!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew/dexter.png&quot; class=&quot;bare&quot; alt=&quot;Macro Manager&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Macro Manager&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;There’s my little guy!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You can abort a running macro with &lt;kbd&gt;esc&lt;/kbd&gt;. For some
reason it will say “aborted by hacker” lol?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;aside id=&quot;footnotes&quot; class=&quot;footnotes footnotes-end-of-document&quot; role=&quot;doc-endnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;&lt;p&gt;Yes, I broke it on purpose for this demonstation!&lt;a href=&quot;https://lock.cmpxchg8b.com/hiew.html#fnref1&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/aside&gt;



</description><link>https://lock.cmpxchg8b.com/hiew.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/hiew.html</guid><author>Tavis Ormandy</author></item><item><title>What happens when a CPU goes wrong?</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Reptar&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;We have a CPU mystery! We found a way to cause some processors to
enter a glitch state where the normal rules don’t apply, but what does
that mean…?&lt;/p&gt;
&lt;p&gt;If you’re interested what can go wrong inside modern CPUs, read
on!&lt;/p&gt;
&lt;section id=&quot;introduction&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;If you’ve ever written any x86 assembly at all, you’ve probably used
&lt;code&gt;rep movsb&lt;/code&gt;. It’s the idiomatic way of moving memory around
on x86. You set the &lt;em&gt;source&lt;/em&gt;, &lt;em&gt;destination&lt;/em&gt;,
&lt;em&gt;direction&lt;/em&gt; and the &lt;em&gt;count&lt;/em&gt; - then just let the processor
handle all the details!&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode nasm&quot;&gt;&lt;code class=&quot;sourceCode nasm&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb1-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;lea&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;rel&lt;/span&gt; dst&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb1-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;lea&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;rel&lt;/span&gt; src&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb1-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;std&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb1-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rcx&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;32&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb1-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; movsb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The actual instruction here is &lt;code&gt;movsb&lt;/code&gt;, the
&lt;code&gt;rep&lt;/code&gt; is simply a prefix that changes how the instruction
works. In this case, it indicates that you want this operation
&lt;strong&gt;rep&lt;/strong&gt;eated multiple times.&lt;/p&gt;
&lt;p&gt;There are lots of other prefixes too, but they don’t all apply to
every instruction.&lt;/p&gt;
&lt;section id=&quot;prefix-decoding&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Prefix Decoding&lt;/h4&gt;
&lt;p&gt;An interesting feature of x86 is that the instruction decoding is
generally quite relaxed. If you use a prefix that doesn’t make sense or
conflicts with other prefixes nothing much will happen, it will usually
just be ignored.&lt;/p&gt;
&lt;p&gt;This fact is sometimes useful; compilers can use redundant prefixes
to pad a single instruction to a desirable alignment boundary.&lt;/p&gt;
&lt;p&gt;Take a look at this snippet, this is exactly the same code as above,
just a bunch of useless or redundant prefixes have been added:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode nasm&quot;&gt;&lt;code class=&quot;sourceCode nasm&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; lea &lt;span class=&quot;kw&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;rel&lt;/span&gt; dst&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb2-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;             &lt;span class=&quot;kw&quot;&gt;cs&lt;/span&gt; lea &lt;span class=&quot;kw&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;rel&lt;/span&gt; src&lt;span class=&quot;op&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb2-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;       gs &lt;span class=&quot;kw&quot;&gt;gs&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;gs&lt;/span&gt; std&lt;/span&gt;
&lt;span id=&quot;cb2-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb2-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          &lt;span class=&quot;kw&quot;&gt;repnz&lt;/span&gt; mov &lt;span class=&quot;kw&quot;&gt;rcx&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;32&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb2-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; movsb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Perhaps the most interesting prefixes are &lt;code&gt;rex&lt;/code&gt;,
&lt;code&gt;vex&lt;/code&gt; and &lt;code&gt;evex&lt;/code&gt;, all of which change how
subsequent instructions are decoded.&lt;/p&gt;
&lt;p&gt;Let’s take a look at how they work.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;the-rex-prefix&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;The REX prefix&lt;/h4&gt;
&lt;p&gt;The i386 only had 8 general purpose registers, so you could specify
which register you want to use in just 3 bits (because 2^3 is 8).&lt;/p&gt;
&lt;p&gt;The way that instructions were encoded took advantage of this fact,
and reserved &lt;em&gt;just&lt;/em&gt; enough bits to specify any of those
registers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/rep-modrm.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;modr/m example&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;modr/m example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Simple 2-byte instructions that use modr/m might be encoded like
this, for example &lt;code&gt;mov eax, ebx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is an 8-bit opcode, 2 bit addressing mode (labeled m), and 3
bits each for the source (s) and destination (d).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, this is a problem, because x86-64 added 8 additional general
purpose registers. We now have sixteen possible registers..that’s 2^4,
so we’re going to need another bit! 😆&lt;/p&gt;
&lt;p&gt;The solution to this is the &lt;code&gt;rex&lt;/code&gt; prefix, which gives us
some spare bits that the next instruction can borrow.&lt;/p&gt;
&lt;p&gt;When we’re talking about rex, we usually write it like this:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode nasm&quot;&gt;&lt;code class=&quot;sourceCode nasm&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb3-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;rex&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;rxb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;rex&lt;/code&gt; is a single-byte prefix, the first four bits are
mandatory and the remaining four bits called &lt;code&gt;b&lt;/code&gt;,
&lt;code&gt;x&lt;/code&gt;, &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;w&lt;/code&gt; are all optional. If
you see &lt;code&gt;rex.rb&lt;/code&gt; that means only the &lt;code&gt;r&lt;/code&gt; and
&lt;code&gt;b&lt;/code&gt; bits are set, all the others are unset.&lt;/p&gt;
&lt;p&gt;These optional bits give us room to encode more general purpose
registers in the following instruction.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/rep-rexmodrm.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;rex example&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;rex example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;rex&lt;/code&gt; prefix can lend the next instruction extra bits
to use for operands, so now we can encode all 16 possible general
purpose registers!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we’re fine until someone &lt;a href=&quot;https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-performance-extensions-apx.html&quot;&gt;adds
another register&lt;/a&gt;! 😂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;encoding-rules&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Encoding Rules&lt;/h4&gt;
&lt;p&gt;So now we know that &lt;code&gt;rex&lt;/code&gt; increases the available space
for encoding operands, and that useless or redundant prefixes are
usually ignored on x86. So… what should this instruction do?&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre class=&quot;sourceCode nasm&quot;&gt;&lt;code class=&quot;sourceCode nasm&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/reptar.html#cb4-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;rex&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;rxb &lt;span class=&quot;kw&quot;&gt;rep&lt;/span&gt; movsb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;movsb&lt;/code&gt; instruction doesn’t have any operands -
they’re all implicit - so any &lt;code&gt;rex&lt;/code&gt; bits are meaningless,
right?&lt;/p&gt;
&lt;p&gt;If you guessed that the processor will just silently ignore the
&lt;code&gt;rex&lt;/code&gt; prefix, you would be correct!&lt;/p&gt;
&lt;p&gt;Well… except on machines that support a new feature called &lt;em&gt;fast
short repeat move&lt;/em&gt;! We discovered that a bug with redundant
&lt;code&gt;rex&lt;/code&gt; prefixes could interact with this feature in an
unexpected way and introduce a serious vulnerability, oops 🙂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;fast-short-repeat-move&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Fast Short Repeat Move&lt;/h4&gt;
&lt;p&gt;FSRM is a new feature introduced in &lt;a href=&quot;https://en.wikichip.org/wiki/intel/microarchitectures/ice_lake_(client)#New_instructions&quot;&gt;Ice
Lake&lt;/a&gt; that fixes some of the shortcomings of ERMS. Hopefully that
clears up any confusion. 😆&lt;/p&gt;
&lt;p&gt;Just kidding, let’s quickly look at ERMS.&lt;/p&gt;
&lt;p&gt;The hard part of moving strings around efficiently is getting all the
buffers aligned so you can use the widest possible stores available. You
&lt;em&gt;could&lt;/em&gt; do this in software, but if we do it in microcode then
the processor can just transparently make your existing code faster for
you.&lt;/p&gt;
&lt;p&gt;This requires some expensive setup, but once that’s done you get
vastly improved throughput. This feature is known as &lt;em&gt;enhanced repeat
move/store&lt;/em&gt;, ERMS.&lt;/p&gt;
&lt;p&gt;If you have a processor with ERMS support, simple
&lt;code&gt;rep movsb&lt;/code&gt; operations can sometimes perform comparably with
more complicated hand-tuned vector move operations.&lt;/p&gt;
&lt;p&gt;However, there is a problem with ERMS. That initial setup is so
expensive that it just isn’t worth it for very short strings. This is
what FSRM is designed to solve, it handles the case of only moving 128
bytes or less and makes that faster too!&lt;/p&gt;
&lt;p&gt;I’m not aware of any documentation that explains exactly how FSRM
works, but you can check if you have a processor that supports it by
looking at the flags line in &lt;code&gt;/proc/cpuinfo&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flags       : fpu vme de pse tsc msr pae mce cx8 [...] fsrm&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some of the processors that have this feature include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ice Lake&lt;/li&gt;
&lt;li&gt;Rocket Lake&lt;/li&gt;
&lt;li&gt;Tiger Lake&lt;/li&gt;
&lt;li&gt;Raptor Lake&lt;/li&gt;
&lt;li&gt;Alder Lake&lt;/li&gt;
&lt;li&gt;Sapphire Rapids&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This list may not be comprehensive, please see Intel advisory
INTEL-SA-00950 for a complete list.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;discovery&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Discovery&lt;/h2&gt;
&lt;p&gt;I’ve written previously about a processor validation technique called
&lt;em&gt;Oracle Serialization&lt;/em&gt; that we’ve been using. The idea is to
generate two forms of the same randomly generated program and verify
their final state is identical.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read more about Oracle Serialization in my &lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html&quot;&gt;previous writeup&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In August, our validation pipeline produced an interesting assertion.
It had found a case where adding redundant &lt;code&gt;rex.r&lt;/code&gt; prefixes
to an FSRM optimized &lt;code&gt;rep movs&lt;/code&gt; operation seemed to cause
unpredictable results.&lt;/p&gt;
&lt;p&gt;We observed some very strange behavior while testing. For example,
branches to unexpected locations, unconditional branches being ignored
and the processor no longer accurately recording the instruction pointer
in &lt;code&gt;xsave&lt;/code&gt; or &lt;code&gt;call&lt;/code&gt; instructions.&lt;/p&gt;
&lt;p&gt;Oddly, when trying to understand what was happening we would see a
debugger reporting impossible states!&lt;/p&gt;
&lt;p&gt;This already seemed like it could be indicative of a serious problem,
but within a few days of experimenting we found that when multiple cores
were triggering the same bug, the processor would begin to report
machine check exceptions and halt.&lt;/p&gt;
&lt;p&gt;We verified this worked even inside an unprivileged guest VM, so this
already has serious security implications for cloud providers.
Naturally, we reported this to Intel as soon as we confirmed this was a
security issue.&lt;/p&gt;
&lt;section id=&quot;reproduce&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Reproduce&lt;/h4&gt;
&lt;p&gt;We’re publishing all of our research today to our &lt;a href=&quot;https://github.com/google/security-research/tree/master/pocs/cpus/reptar&quot;&gt;security
research repository&lt;/a&gt;. If you want to reproduce the vulnerability you
can use our &lt;code&gt;icebreak&lt;/code&gt; tool, I’ve also made a local mirror
available &lt;a href=&quot;https://lock.cmpxchg8b.com/files/icebreak.tar.gz&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./icebreak -h
usage: ./icebreak [OPTIONS]
    -c N,M      Run repro threads on core N and M.
    -d N        Sleep N usecs between repro attempts.
    -H N        Spawn a hammer thread on core N.
icebreak: you must at least specify a core pair with -c! (see -h for help)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The testcase enters what should be an infinite loop, and unaffected
systems should see no output at all. On affected systems, a
&lt;code&gt;.&lt;/code&gt; is printed on each successful reproduction.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./icebreak -c 0,4
starting repro on cores 0 and 4
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In general, if the cores are
&lt;abbr title=&quot;Symmetric Multithreading&quot;&gt;SMT&lt;/abbr&gt; siblings then you may
observe random branches and if they’re &lt;abbr title=&quot;Symmetric Multiprocessing&quot;&gt;SMP&lt;/abbr&gt; siblings from the same
package then you may observe machine checks.&lt;/p&gt;
&lt;p&gt;If you do &lt;em&gt;not&lt;/em&gt; specify two different cores, then you might
need to use a hammer thread to trigger a reproduction.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;analysis&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Analysis&lt;/h4&gt;
&lt;p&gt;We know something strange is happening, but how microcode works in
modern systems is a closely guarded secret. We can only theorize about
the root cause based on observations.&lt;/p&gt;
&lt;section id=&quot;μops&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;μops&lt;/h5&gt;
&lt;p&gt;The CPU is split in two major components, the &lt;em&gt;frontend&lt;/em&gt; and
the &lt;em&gt;backend&lt;/em&gt;. The frontend is responsible for fetching
instructions, decoding them and generating μops to send to the backend
for execution.&lt;/p&gt;
&lt;p&gt;The backend executes instructions &lt;em&gt;out of order&lt;/em&gt;, and uses a
unit called the ROB, &lt;em&gt;reorder buffer&lt;/em&gt;, to store and organize
results.&lt;/p&gt;
&lt;p&gt;We believe this bug causes the frontend to miscalculate the size of
the &lt;code&gt;movsb&lt;/code&gt; instruction, causing subsequent entries in the
ROB to be associated with incorrect addresses. When this happens, the
CPU enters a confused state that causes the instruction pointer to be
miscalculated.&lt;/p&gt;
&lt;p&gt;The machine can eventually recover from this state, perhaps with
incorrect intermediate results, but becoming internally consistent
again. However, if we cause multiple SMT or SMP cores to enter the state
simultaneously, we can cause enough microarchitectural state corruption
to force a machine check.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;questions&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Questions&lt;/h4&gt;
&lt;p&gt;I’m sure some readers will have questions about what is possible in
this unexpected “glitch” state. Well, so do we!&lt;/p&gt;
&lt;p&gt;We know that we can corrupt the system state badly enough to cause
machine check errors, and we’ve also observed threads interfere with
execution of processes scheduled on SMT siblings.&lt;/p&gt;
&lt;p&gt;However, we simply don’t know if we can control the corruption
precisely enough to achieve privilege escalation. I suspect that it
&lt;em&gt;is&lt;/em&gt; possible, but we don’t have any way to debug μop
execution!&lt;/p&gt;
&lt;p&gt;If you’re interested in studying this, then we would love to get your
input!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;credit&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Credit&lt;/h4&gt;
&lt;p&gt;This bug was independently discovered by multiple research teams
within Google, including the &lt;a href=&quot;https://github.com/google/silifuzz&quot;&gt;silifuzz&lt;/a&gt; team and Google
&lt;a href=&quot;https://bughunters.google.com/blog&quot;&gt;Information Security
Engineering&lt;/a&gt;. The bug was analyzed by Tavis Ormandy, Josh Eads,
Eduardo Vela Nava, Alexandra Sandulescu and Daniel Moghimi.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;solution&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Intel have &lt;a href=&quot;https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00950.html&quot;&gt;published&lt;/a&gt;
updated microcode for all affected processors. Your operating system or
BIOS vendor may already have an update available!&lt;/p&gt;
&lt;section id=&quot;workaround&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Workaround&lt;/h4&gt;
&lt;p&gt;If you can’t update for some reason, you &lt;em&gt;could&lt;/em&gt; disable fast
strings via the &lt;code&gt;IA32_MISC_ENABLE&lt;/code&gt; model specific
register.&lt;/p&gt;
&lt;p&gt;This will cause a significant performance penalty, and should not be
used unless absolutely necessary.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;notes&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;p&gt;If you’re interested in more CPU bugs, we publish everything we
find!&lt;/p&gt;
&lt;p&gt;Not all the bugs we discover have security consequences, but they’re
usually worth reading! For example, did you know that sometimes &lt;a href=&quot;https://github.com/google/security-research/tree/master/pocs/cpus/errata/amd/genoa-lps-hps&quot;&gt;movlps
just doesn’t work&lt;/a&gt;? or that registers can sometimes &lt;a href=&quot;https://github.com/google/security-research/tree/master/pocs/cpus/errata/amd/1386&quot;&gt;roll
back&lt;/a&gt; to previous values?&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/reptar.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/reptar.html</guid><author>Tavis Ormandy</author></item><item><title>An overview of hacking the Timex m851.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Hacking the Timex m851&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;&lt;small&gt; &lt;em&gt;This post is part of a series on my quest to find the
perfect watch.&lt;/em&gt; &lt;/small&gt;&lt;/p&gt;
&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/datalink.jpg&quot; title=&quot;Timex m851&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/datalink.jpg&quot; alt=&quot;Timex m851&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Take a look at this watch, it’s just some boring watch for runners,
right?&lt;/p&gt;
&lt;p&gt;Nope, I think this might be the best ultra-low power consumer digital
watch ever produced!&lt;/p&gt;
&lt;p&gt;Let me explain…&lt;/p&gt;
&lt;section id=&quot;specifications&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Specifications&lt;/h4&gt;
&lt;p&gt;This is the Timex m851. It uses an 8-bit Seiko SC188 CPU, has 48KB of
ROM, 2KB of RAM and a 42x11 dot matrix main display.&lt;/p&gt;
&lt;p&gt;The cpu is designed for ultra-low power operation - a single battery
can last 3 &lt;em&gt;years&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;This is a big selling point for me, I don’t think they’re making
consumer watches like this any more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/datalink-usb.jpg&quot; class=&quot;noclear&quot; alt=&quot;Timex USB&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/datalink-pins.jpg&quot; class=&quot;noclear&quot; alt=&quot;Timex USB&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;Those pins on the side? That’s USB. Naturally, you can synchronize
things like appointments, and so on (there’s a neat &lt;a href=&quot;https://github.com/HuidaeCho/libdlusb&quot;&gt;linux library&lt;/a&gt; for
that).&lt;/p&gt;
&lt;p&gt;Here’s where it get’s interesting… this thing has an SDK, and it is
&lt;em&gt;surprisingly&lt;/em&gt; good!&lt;/p&gt;
&lt;p&gt;You can just &lt;code&gt;$ cc helloworld.c&lt;/code&gt;, and upload it to your
watch.&lt;/p&gt;
&lt;p&gt;Timex provided a Windows XP-era wizard to develop applications, and
it is &lt;strong&gt;&lt;em&gt;painful&lt;/em&gt;&lt;/strong&gt;. However, it turned out that was
just driving a UNIX-like toolchain behind the scenes. After pulling that
out, hacking this started to become pretty fun!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/datalink-make.gif&quot; style=&quot;width:70.0%&quot; alt=&quot;Makefile&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/watch.gif&quot; style=&quot;width:20.0%&quot; alt=&quot;Hello World&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;watchapps&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;WatchApps&lt;/h1&gt;
&lt;p&gt;Just want to see some code? No problem, I’ve uploaded a &lt;em&gt;hello
world&lt;/em&gt; and a &lt;code&gt;Makefile&lt;/code&gt; to build it to github:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/taviso/timex/&quot;&gt;https://github.com/taviso/timex/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to browse the full manual, there are some links
below.&lt;/p&gt;
&lt;p&gt;You can do some fun things with this, people wrote all kinds of &lt;a href=&quot;https://www.youtube.com/watch?v=mitJafrsimw&quot;&gt;games&lt;/a&gt;, &lt;a href=&quot;http://datalinkwristapps.free.fr/index.php5?n=Wristapps.MyWatch&quot;&gt;utilities&lt;/a&gt;,
and &lt;a href=&quot;https://rayslogic.com/Software/TimexUSB/WristApps/WristApps.htm&quot;&gt;tools&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want a TL;DR of development, I’ll try!&lt;/p&gt;
&lt;section id=&quot;design&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Design&lt;/h4&gt;
&lt;section id=&quot;states-and-modes&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;States and Modes&lt;/h5&gt;
&lt;p&gt;You need to split your application into &lt;em&gt;states&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;state&lt;/em&gt; is just a way to swap code in and out as needed.
When you switch states, the active code is discarded and the new code is
swapped in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; write an app that uses up to ~30kb of code and/or
data, &lt;em&gt;but&lt;/em&gt; there is only ~2kb of RAM. This is why you need to
use states, there is no paging and you couldn’t fit that in!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is room for some &lt;em&gt;common&lt;/em&gt; code that all your states
share, and of course there is space for variables, along with a database
API for persistent data.&lt;/p&gt;
&lt;p&gt;The first few states are reserved for handling common events. The
rest you can use for anything you want - you just ask the kernel to
switch you whenever you need it!&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb1-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; bar&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb1-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    coreRequestStateChange&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;FOOBAR_STATE&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb1-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;em&gt;mode&lt;/em&gt; is just a foreground application.&lt;/p&gt;
&lt;p&gt;Your app is (probably) providing a mode, but you can also add
background tasks, periodic tasks, and so on.&lt;/p&gt;
&lt;p&gt;Requesting a mode change is sort of like &lt;code&gt;exit()&lt;/code&gt;, the
next app will gain control.&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;coreRequestModeChangeNext&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;events&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Events&lt;/h5&gt;
&lt;p&gt;Each state needs an event handler. A super simple one would be like
this:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt; default_state_manager&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;CORECurrentEvent&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// This is called when this state becomes active.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;case&lt;/span&gt; COREEVENT_STATEENTRY&lt;span class=&quot;op&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            coreEnableSwitchReleaseEvents&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            coreAllowKeys&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;COREALLOWALLSWITCHES&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// This is called when the user pulls the crown out.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-10&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// When they push it back in, you get a CROWN_HOME event.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-11&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// It can be rotated in either home or set positions.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-12&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;case&lt;/span&gt; COREEVENT_CROWN_SET&lt;span class=&quot;op&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-13&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-13&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            coreRequestStateChange&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;CORESETBANNERSTATE&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-14&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-14&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-15&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-15&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// You can get button events too, like start/split:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-16&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-16&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;case&lt;/span&gt; COREEVENT_STARTSPLITDEPRESS&lt;span class=&quot;op&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-17&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-17&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            show_message&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-18&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-18&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-19&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-19&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;co&quot;&gt;// The mode button probably means the user wants to exit...&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-20&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-20&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;case&lt;/span&gt; COREEVENT_MODEDEPRESS&lt;span class=&quot;op&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-21&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-21&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            coreRequestModeChangeNext&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-22&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-22&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-23&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-23&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-24&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-24&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-25&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-25&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-26&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb3-26&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In general, you can ignore any event you don’t care about.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;services&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Services&lt;/h5&gt;
&lt;p&gt;The kernel takes care of handling the hardware, dispatching events to
you and provides various services.&lt;/p&gt;
&lt;p&gt;Things like timers, generating tones (beeps), scrolling the display,
access to database records, etc.&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;uint8_t&lt;/span&gt; banner&lt;span class=&quot;op&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    LCDBANNER_COL10&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    DM5_H&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; DM5_E&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; DM5_L&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; DM5_L&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; DM5_O&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; DM5_EXCLAMATION&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    LCD_END_BANNER&lt;/span&gt;
&lt;span id=&quot;cb4-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;lcdClearDisplay&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// Draw &quot;hello!&quot; using kernel services.&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-10&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/timex.html#cb4-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;lcdDispBannerMsg&lt;span class=&quot;op&quot;&gt;(&amp;amp;&lt;/span&gt;banner&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The watch has a crown - and you can ask the kernel to configure it in
a few different ways. For example, you might want pulse mode so you can
monitor how quickly the user is turning it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/crown.gif&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;Crown Demo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Crown Demo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;It has a backlight, you can control that from software too.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;debugging&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Debugging&lt;/h4&gt;
&lt;p&gt;There’s no memory protection or anything, you can just clobber the
kernel if you want… so what happens if you crash?&lt;/p&gt;
&lt;p&gt;Well, one thing the kernel does is notify a watchdog every 2 seconds
that it’s still alive. If the watchdog doesn’t see that notification, it
will just reset the watch… so try not to do that! 😀&lt;/p&gt;
&lt;p&gt;If you’re doing some heavy computation you could accidentally trigger
the watchdog. Just remember to let the watchdog know you’re still
alive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; // Let the watchdog know we&#39;re not dead.
 hwResetWatchdog();&lt;/code&gt;&lt;/pre&gt;
&lt;section id=&quot;simulator&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Simulator&lt;/h5&gt;
&lt;p&gt;Luckily there’s a really good simulator available, so you don’t have
to sit through tedious resets everytime you make a typo. It’s actually
an open source 3rd party tool, &lt;a href=&quot;http://datalinkwristapps.free.fr/index.php5?n=PC.VirtualDatalink&quot;&gt;Virtual
Datalink&lt;/a&gt; (&lt;a href=&quot;https://sourceforge.net/projects/virtualdatalink/&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sadly it’s for Windows only. I would port it to Linux, but it’s
written in Delphi… is it feasible to port it to free pascal? Let me
know!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It has conditional breakpoints, a dissasembler, save states, resource
and power analysis. I’ve found it pretty capable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/vdatalink.gif&quot; class=&quot;noborder&quot; alt=&quot;Virtual Datalink&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Virtual Datalink&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The main app I’m working on right now is integrating my watch with &lt;a href=&quot;https://dianne.skoll.ca/projects/remind/&quot;&gt;remind&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hacking this little watch has been a super fun project!&lt;/p&gt;
&lt;section id=&quot;availability&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Availability&lt;/h4&gt;
&lt;p&gt;Now that I’ve told you how much I love this thing - the bad news.&lt;/p&gt;
&lt;p&gt;They’re long out of production, and getting hard to come by.&lt;/p&gt;
&lt;p&gt;I bought a set of two on eBay, they just needed new batteries and
were as good as new.&lt;/p&gt;
&lt;p&gt;If this is the sort of thing that appeals to you, setup an alert and
then help me hack on it 😀&lt;/p&gt;
&lt;section id=&quot;tips&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Tips&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Found a watch but missing the cable? It uses the standard USB type-a
pinout with an odd connector, you can probably just use an old USB cable
and alligator clips.&lt;/li&gt;
&lt;li&gt;Timex used the same connector on a much less desirable device called
the T5G751 (probably other model variants too). These are easy to find
at reasonable prices (often &amp;lt; $10 at the time of writing), so you can
simply toss the device and keep the cable (&lt;a href=&quot;https://www.ebay.com/itm/284347685668?nordt=true&quot;&gt;example&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;There was a m851 variant called the “dress edition”, it has
identical specs but a different body. It is sometimes listed with a
different model number (&lt;a href=&quot;https://www.ebay.com/itm/134839596133?nordt=true&quot;&gt;example&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;references&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;References&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The most important doc is the &lt;a href=&quot;https://assets.timex.com/developer/developer_downloads/WA_Design_Guide.pdf&quot;&gt;Design
Guide&lt;/a&gt;, it’s very well written.&lt;/li&gt;
&lt;li&gt;Then of course, you need the &lt;a href=&quot;https://www.rayslogic.com/Software/TimexUSB/Docs/s1c88%20core%20cpu%20manual.pdf&quot;&gt;CPU
manual&lt;/a&gt;. It’s also excellent!&lt;/li&gt;
&lt;li&gt;The toolchain also has a thorough manual.&lt;/li&gt;
&lt;li&gt;Finally, the complete &lt;a href=&quot;https://assets.timex.com/developer/developer_downloads/WA_API_Reference.pdf&quot;&gt;API
reference&lt;/a&gt; lists the services provided by the Kernel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/timex.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/timex.html</guid><author>Tavis Ormandy</author></item><item><title>I found an interesting CPU bug.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Zenbleed&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: 7d85ef516b9682eaf0db09cc9c4e53a523486f3f $&lt;/p&gt;
&lt;/header&gt;

&lt;p&gt;If you remove the first word from the string
&lt;code&gt;&quot;hello world&quot;&lt;/code&gt;, what should the result be? This is the story
of how we discovered that the answer could be your root password!&lt;/p&gt;
&lt;section id=&quot;introduction&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;All x86-64 CPUs have a set of 128-bit vector registers called the
&lt;code&gt;XMM&lt;/code&gt; registers. You can never have enough bits, so recent
CPUs have extended the width of those registers up to 256-bit and even
512-bits.&lt;/p&gt;
&lt;p&gt;The 256-bit extended registers are called &lt;code&gt;YMM&lt;/code&gt;, and the
512-bit registers are &lt;code&gt;ZMM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These big registers are useful in lots of situations, not just number
crunching! They’re even used by standard C library functions, like
&lt;code&gt;strcmp&lt;/code&gt;, &lt;code&gt;memcpy&lt;/code&gt;, &lt;code&gt;strlen&lt;/code&gt; and so
on.&lt;/p&gt;
&lt;p&gt;Let’s take a look at an example. Here are the first few instructions
of glibc’s AVX2 optimized &lt;code&gt;strlen&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) x/20i __strlen_avx2
...
   &amp;lt;__strlen_avx2+9&amp;gt;:   vpxor  xmm0,xmm0,xmm0
...
   &amp;lt;__strlen_avx2+29&amp;gt;:  vpcmpeqb ymm1,ymm0,YMMWORD PTR [rdi]
   &amp;lt;__strlen_avx2+33&amp;gt;:  vpmovmskb eax,ymm1
...
   &amp;lt;__strlen_avx2+41&amp;gt;:  tzcnt  eax,eax
   &amp;lt;__strlen_avx2+45&amp;gt;:  vzeroupper
   &amp;lt;__strlen_avx2+48&amp;gt;:  ret&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full routine is complicated and handles lots of cases, but let’s
step through this simple case. Bear with me, I promise there’s a
point!&lt;/p&gt;
&lt;p&gt;The first step is to initialize &lt;code&gt;ymm0&lt;/code&gt; to zero, which is
done by just &lt;code&gt;xor&lt;/code&gt;ing &lt;code&gt;xmm0&lt;/code&gt; with itself&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fn1&quot; class=&quot;footnote-ref&quot; id=&quot;fnref1&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-vpxor.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;VPXOR xmm0, xmm0, xmm0&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;VPXOR xmm0, xmm0, xmm0&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;    &amp;gt; vpxor xmm0, xmm0, xmm0
      vpcmpeqb ymm1, ymm0, [rdi]
      vpmovmskb eax, ymm1
      tzcnt eax, eax
      vzeroupper&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here &lt;code&gt;rdi&lt;/code&gt; contains a pointer to our string, so
&lt;code&gt;vpcmpeqb&lt;/code&gt; will check which bytes in &lt;code&gt;ymm0&lt;/code&gt; match
our string, and stores the result in &lt;code&gt;ymm1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As we’ve already set &lt;code&gt;ymm0&lt;/code&gt; to all zero bytes, only nul
bytes will match.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-vpcmpeqb.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;vpcmpeqb ymm1, ymm0, rdi&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;vpcmpeqb ymm1, ymm0, rdi&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;      vpxor xmm0, xmm0, xmm0
    &amp;gt; vpcmpeqb ymm1, ymm0, [rdi]
      vpmovmskb eax, ymm1
      tzcnt eax, eax
      vzeroupper&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we can extract the result into a general purpose register like
&lt;code&gt;eax&lt;/code&gt; with &lt;code&gt;vpmovmskb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any nul byte will create a 1 bit, and any other value will create a 0
bit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-vpmovmskb.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;vpmovmskb eax, ymm1&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;vpmovmskb eax, ymm1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;      vpxor xmm0, xmm0, xmm0
      vpcmpeqb ymm1, ymm0, [rdi]
    &amp;gt; vpmovmskb eax, ymm1
      tzcnt eax, eax
      vzeroupper&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finding the first zero byte is now just a case of counting the number
of trailing zero bits.&lt;/p&gt;
&lt;p&gt;That’s a common enough operation that there’s an instruction for it -
&lt;code&gt;tzcnt&lt;/code&gt; (Trailing Zero Count).&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-tzcnt.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;tzcnt eax, eax&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;tzcnt eax, eax&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;      vpxor xmm0, xmm0, xmm0
      vpcmpeqb ymm1, ymm0, [rdi]
      vpmovmskb eax, ymm1
    &amp;gt; tzcnt eax, eax
      vzeroupper&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we have the position of the first nul byte, in just four machine
instructions!&lt;/p&gt;
&lt;p&gt;You can probably imagine just how often &lt;code&gt;strlen&lt;/code&gt; is
running on your system right now, but suffice to say, bits and bytes are
flowing into these vector registers from all over your system
constantly.&lt;/p&gt;
&lt;section id=&quot;zeroing-registers&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Zeroing Registers&lt;/h4&gt;
&lt;p&gt;You might have noticed that I missed one instruction, and that’s
&lt;code&gt;vzeroupper&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-vzeroupper.svg&quot; class=&quot;noborder&quot; style=&quot;width:100.0%&quot; alt=&quot;vzeroupper&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;vzeroupper&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;      vpxor xmm0, xmm0, xmm0
      vpcmpeqb ymm1, ymm0, [rdi]
      vpmovmskb eax, ymm1
      tzcnt eax, eax
    &amp;gt; vzeroupper&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You guessed it, &lt;code&gt;vzeroupper&lt;/code&gt; will zero the upper bits of
the vector registers.&lt;/p&gt;
&lt;p&gt;The reason we do this is because if you mix &lt;code&gt;XMM&lt;/code&gt; and
&lt;code&gt;YMM&lt;/code&gt; registers, the &lt;code&gt;XMM&lt;/code&gt; registers automatically
get promoted to full width. It’s a bit like integer promotion in C.&lt;/p&gt;
&lt;p&gt;This works fine, but &lt;a href=&quot;https://en.wikipedia.org/wiki/Superscalar&quot;&gt;superscalar&lt;/a&gt;
processors need to track dependencies so that they know which operations
can be parallelized. This promotion adds a dependency on those upper
bits, and that causes unnecessary stalls while the processor waits for
results it didn’t really need.&lt;/p&gt;
&lt;p&gt;These stalls are what glibc is trying to avoid with
&lt;code&gt;vzeroupper&lt;/code&gt;. Now any future results won’t depend on what
those bits are, so we safely avoid that bottleneck!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;the-vector-register-file&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;The Vector Register File&lt;/h4&gt;
&lt;p&gt;Now that we know &lt;em&gt;what&lt;/em&gt; &lt;code&gt;vzeroupper&lt;/code&gt; does,
&lt;em&gt;how&lt;/em&gt; does it do it?&lt;/p&gt;
&lt;p&gt;Your processor doesn’t have a single physical location where each
register lives, it has what’s called a &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Register_file&quot;&gt;Register
File&lt;/a&gt;&lt;/em&gt; and a &lt;em&gt;Register Allocation Table&lt;/em&gt;. This is a bit
like managing the heap with &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt;,
if you think of each register as a pointer. The RAT keeps track of what
space in the register file is assigned to which register.&lt;/p&gt;
&lt;p&gt;In fact, when you zero an &lt;code&gt;XMM&lt;/code&gt; register, the processor
doesn’t store those bits anywhere at all - it just sets a flag called
the &lt;a href=&quot;https://en.wikichip.org/wiki/amd/microarchitectures/zen_2#Floating_Point_Unit&quot;&gt;z-bit&lt;/a&gt;
in the RAT. This flag can be applied to the upper and lower parts of
&lt;code&gt;YMM&lt;/code&gt; registers independently, so &lt;code&gt;vzeroupper&lt;/code&gt; can
simply set the z-bit and then release any resources assigned to it in
the register file.&lt;/p&gt;
&lt;figure&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-zbit.svg&quot; class=&quot;noborder&quot; alt=&quot;Z-Bit&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Z-Bit&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;visible&quot;&gt;
A register allocation table (left) and a physical register file (right).
&lt;/figcaption&gt;
&lt;/blockquote&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id=&quot;speculation&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Speculation&lt;/h4&gt;
&lt;p&gt;Hold on, there’s another complication! Modern processors use &lt;a href=&quot;https://en.wikipedia.org/wiki/Speculative_execution&quot;&gt;speculative
execution&lt;/a&gt;, so sometimes operations have to be rolled back.&lt;/p&gt;
&lt;p&gt;What should happen if the processor &lt;em&gt;speculatively&lt;/em&gt; executed a
&lt;code&gt;vzeroupper&lt;/code&gt;, but then discovers that there was a branch
misprediction? Well, we will have to revert that operation and put
things back the way they were… maybe we can just unset that z-bit?&lt;/p&gt;
&lt;p&gt;If we return to the analogy of &lt;code&gt;malloc&lt;/code&gt; and
&lt;code&gt;free&lt;/code&gt;, you can see that it can’t be that simple - that would
be like calling &lt;code&gt;free()&lt;/code&gt; on a pointer, and then changing your
mind!&lt;/p&gt;
&lt;p&gt;That would be a use-after-free vulnerability, but there is no such
thing as a use-after-free in a CPU… or is there?&lt;/p&gt;
&lt;p&gt;Spoiler: &lt;strong&gt;&lt;em&gt;yes there is&lt;/em&gt;&lt;/strong&gt; 🙂&lt;/p&gt;
&lt;figure&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-leaking.gif&quot; class=&quot;noborder&quot; alt=&quot;Zenbleed Demo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Zenbleed Demo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;visible&quot;&gt;
This animation shows why resetting the z-bit is not sufficient.
&lt;/figcaption&gt;
&lt;/blockquote&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;vulnerability&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;It turns out that with precise scheduling, you &lt;em&gt;can&lt;/em&gt; cause
some processors to recover from a mispredicted &lt;code&gt;vzeroupper&lt;/code&gt;
incorrectly!&lt;/p&gt;
&lt;p&gt;This technique is CVE-2023-20593 and it works on all Zen 2 class
processors, which includes at least the following products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AMD Ryzen 3000 Series Processors&lt;/li&gt;
&lt;li&gt;AMD Ryzen PRO 3000 Series Processors&lt;/li&gt;
&lt;li&gt;AMD Ryzen Threadripper 3000 Series Processors&lt;/li&gt;
&lt;li&gt;AMD Ryzen 4000 Series Processors with Radeon Graphics&lt;/li&gt;
&lt;li&gt;AMD Ryzen PRO 4000 Series Processors&lt;/li&gt;
&lt;li&gt;AMD Ryzen 5000 Series Processors with Radeon Graphics&lt;/li&gt;
&lt;li&gt;AMD Ryzen 7020 Series Processors with Radeon Graphics&lt;/li&gt;
&lt;li&gt;AMD EPYC “Rome” Processors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The bug works like this, first of all you need to trigger something
called the &lt;em&gt;XMM Register Merge Optimization&lt;/em&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fn2&quot; class=&quot;footnote-ref&quot; id=&quot;fnref2&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;,
followed by a &lt;a href=&quot;https://en.wikipedia.org/wiki/Register_renaming&quot;&gt;register
rename&lt;/a&gt; and a mispredicted &lt;code&gt;vzeroupper&lt;/code&gt;. This all has to
happen within a precise window to work.&lt;/p&gt;
&lt;p&gt;We now know that basic operations like &lt;code&gt;strlen&lt;/code&gt;,
&lt;code&gt;memcpy&lt;/code&gt; and &lt;code&gt;strcmp&lt;/code&gt; will use the vector
registers - so we can effectively spy on those operations happening
anywhere on the system! It doesn’t matter if they’re happening in other
virtual machines, sandboxes, containers, processes, whatever!&lt;/p&gt;
&lt;p&gt;This works because the register file is shared by everything on the
same physical core. In fact, two hyperthreads even share the same
physical register file.&lt;/p&gt;
&lt;p&gt;Don’t believe me? Let’s write an exploit 🙂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;exploitation&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;There are quite a few ways to trigger this, but let’s examine a very
simple example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    vcvtsi2s{s,d}   xmm, xmm, r64
    vmovdqa         ymm, ymm
    jcc             overzero
    vzeroupper
overzero:
    nop&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;cvtsi2sd&lt;/code&gt; is used to trigger the merge optimization.
It’s not important what &lt;code&gt;cvtsi2sd&lt;/code&gt; is &lt;em&gt;supposed&lt;/em&gt; to
do, I’m just using it because it’s one of the instructions the manual
says use that optimization&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fn3&quot; class=&quot;footnote-ref&quot; id=&quot;fnref3&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then we need to trigger a &lt;a href=&quot;https://en.wikipedia.org/wiki/Register_renaming&quot;&gt;register
rename&lt;/a&gt;, &lt;code&gt;vmovdqa&lt;/code&gt; will work. If the conditional branch&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fn4&quot; class=&quot;footnote-ref&quot; id=&quot;fnref4&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt; is taken but the CPU predicts the
not-taken path, the &lt;code&gt;vzeroupper&lt;/code&gt; will be mispredicted and the
bug occurs!&lt;/p&gt;
&lt;section id=&quot;optimization&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Optimization&lt;/h4&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/zb-exploit.gif&quot; class=&quot;noborder&quot; alt=&quot;Exploit Running&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Exploit Running&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It turns out that mispredicting on purpose is difficult to optimize!
It took a bit of work, but I found a variant that can leak about 30 kb
per core, per second.&lt;/p&gt;
&lt;p&gt;This is fast enough to monitor encryption keys and passwords as users
login!&lt;/p&gt;
&lt;p&gt;We’re releasing our full technical advisory, along with all the
associated code today. Full details will be available in our &lt;a href=&quot;https://github.com/google/security-research/tree/master/pocs/cpus&quot;&gt;security
research repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to test the exploit, the code is available &lt;a href=&quot;https://lock.cmpxchg8b.com/files/zenbleed-v5.tar.gz&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that the code is for Linux, but the bug is not dependent on any
particular operating system - all operating systems are affected!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;discovery&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Discovery&lt;/h2&gt;
&lt;p&gt;I found this bug by fuzzing, big surprise 🙂 I’m not the first person
to apply fuzzing techniques to finding hardware flaws. In fact, vendors
fuzz their own products extensively - the industry term for it is
&lt;em&gt;Post-Silicon Validation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So how come this bug wasn’t found earlier? I think I did a couple of
things differently, perhaps with a new perspective as I don’t have an EE
background!&lt;/p&gt;
&lt;section id=&quot;feedback&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Feedback&lt;/h4&gt;
&lt;p&gt;The best performing fuzzers are guided by coverage &lt;a href=&quot;https://google.github.io/clusterfuzz/reference/coverage-guided-vs-blackbox/&quot;&gt;feedback&lt;/a&gt;.
The problem is that there is nothing really analogous to code coverage
in CPUs… However, we do have &lt;a href=&quot;https://en.wikipedia.org/wiki/Hardware_performance_counter&quot;&gt;performance
counters&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;These will let us know when all kinds of &lt;a href=&quot;https://perfmon-events.intel.com/skylake.html&quot;&gt;interesting
architectural events&lt;/a&gt; happen.&lt;/p&gt;
&lt;p&gt;Feeding this data to the fuzzer lets us gently guide it towards
exploring interesting features that we wouldn’t have been able to find
by chance alone!&lt;/p&gt;
&lt;p&gt;It was challenging to get the details right, but I used this to teach
my fuzzer to find interesting instruction sequences. This allowed me to
discover features like merge optimization automatically, without any
input from me!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;oracle&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Oracle&lt;/h4&gt;
&lt;p&gt;When we fuzz software, we’re usually looking for crashes. Software
isn’t supposed to crash, so we know something must have gone wrong if it
does.&lt;/p&gt;
&lt;p&gt;How can we know if a a CPU is executing a randomly generated program
correctly? It might be completely correct for it to crash!&lt;/p&gt;
&lt;p&gt;Well, a few solutions have been proposed to this problem. One
approach is called &lt;a href=&quot;https://ieeexplore.ieee.org/abstract/document/4751878&quot;&gt;reversi&lt;/a&gt;.
The general idea is that for every random instruction you generate, you
also generate the inverse (e.g.&amp;nbsp;&lt;code&gt;ADD r1, r2&lt;/code&gt; →
&lt;code&gt;SUB r1, r2&lt;/code&gt;). Any deviation from the initial state at the
end of execution must have been an error, neat!&lt;/p&gt;
&lt;p&gt;The reversi approach is clever, but it makes generating testcases
very complicated for a CISC architecture like x86.&lt;/p&gt;
&lt;p&gt;A simpler solution is to use an &lt;em&gt;oracle&lt;/em&gt;. An oracle is just
another CPU or a simulator that we can use to check the result. If we
compare the results from our test CPU to our oracle CPU, any mismatch
would suggest that something went wrong.&lt;/p&gt;
&lt;p&gt;I developed a new approach with a combination of these two ideas, I
call it &lt;em&gt;Oracle Serialization&lt;/em&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;oracle-serialization&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Oracle Serialization&lt;/h4&gt;
&lt;p&gt;As developers we monitor the &lt;em&gt;macro-architectural state&lt;/em&gt;,
that’s just things like register values. There is also the
&lt;em&gt;micro-architectural state&lt;/em&gt; which is mostly invisible to us, like
the branch predictor, &lt;a href=&quot;https://en.wikipedia.org/wiki/Out-of-order_execution&quot;&gt;out-of-order
execution&lt;/a&gt; state and the instruction &lt;a href=&quot;https://en.wikipedia.org/wiki/Instruction_pipelining&quot;&gt;pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Serialization lets us have &lt;em&gt;some&lt;/em&gt; control over that, by
instructing the CPU to reset &lt;a href=&quot;https://en.wikipedia.org/wiki/Instruction-level_parallelism&quot;&gt;instruction-level
parallelism&lt;/a&gt;. This includes things like store/load barriers,
speculation fences, cache line flushes, and so on.&lt;/p&gt;
&lt;p&gt;The idea of a Serialized Oracle is to generate a random program, then
automatically transform it into a &lt;a href=&quot;https://www.felixcloutier.com/x86/serialize#description&quot;&gt;serialized&lt;/a&gt;
form.&lt;/p&gt;
&lt;table style=&quot;width:83%;&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 41%&quot;&gt;
&lt;col style=&quot;width: 41%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;code&gt;movnti [rbp+0x0],ebx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;movnti [rbp+0x0],ebx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code color=&quot;green&quot;&gt;sfence&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;code&gt;rcr dh,1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rcr dh,1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code color=&quot;green&quot;&gt;lfence&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;code&gt;sub r10, rax&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sub r10, rax&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code color=&quot;green&quot;&gt;mfence&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;code&gt;rol rbx, cl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rol rbx, cl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;code color=&quot;green&quot;&gt;nop&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td style=&quot;text-align: left;&quot;&gt;&lt;code&gt;xor edi,[rbp-0x57]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xor edi,[rbp-0x57]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
: A randomly generated sequence of instructions, and the same sequence
but with randomized alignment, serialization and speculation fences
added.

&lt;hr&gt;
&lt;p&gt;These two program might have very different performance
characteristics, but they should produce identical output. The
serialized form can now be my oracle!&lt;/p&gt;
&lt;p&gt;If the final states don’t match, then there must have been some error
in how they were executed micro-architecturally - that could indicate a
bug.&lt;/p&gt;
&lt;p&gt;This is exactly how we first discovered this vulnerability, the
output of the serialized oracle didn’t match!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;solution&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;We reported this vulnerability to AMD on the 15th May 2023.&lt;/p&gt;
&lt;p&gt;AMD have &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/commit/?id=0bc3126c9cfa0b8c761483215c25382f831a7c6f&quot;&gt;released&lt;/a&gt;
an microcode update for affected processors. Your BIOS or Operating
System vendor may already have an update available that includes it.&lt;/p&gt;
&lt;section id=&quot;workaround&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Workaround&lt;/h4&gt;
&lt;p&gt;It is highly recommended to use the microcode update.&lt;/p&gt;
&lt;p&gt;If you can’t apply the update for some reason, there is a software
workaround: you can set the &lt;a href=&quot;https://en.wiktionary.org/wiki/chicken_bit&quot;&gt;chicken bit&lt;/a&gt;
&lt;code&gt;DE_CFG[9]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This may have some performance cost.&lt;/p&gt;
&lt;section id=&quot;linux&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Linux&lt;/h5&gt;
&lt;p&gt;You can use &lt;code&gt;msr-tools&lt;/code&gt; to set the chicken bit on all
cores, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# wrmsr -a 0xc0011029 $(($(rdmsr -c 0xc0011029) | (1&amp;lt;&amp;lt;9)))&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id=&quot;freebsd&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;FreeBSD&lt;/h5&gt;
&lt;p&gt;On FreeBSD you would use &lt;code&gt;cpucontrol(8)&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;others&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Others&lt;/h5&gt;
&lt;p&gt;If you’re using some other operating system and don’t know how to set
&lt;abbr title=&quot;Model Specific Registers&quot;&gt;MSRs&lt;/abbr&gt;, ask your vendor for
assistance.&lt;/p&gt;
&lt;p&gt;Note that it is &lt;em&gt;not&lt;/em&gt; sufficient to disable
&lt;abbr title=&quot;Simultaneous
Multithreading&quot;&gt;SMT&lt;/abbr&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;detection&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Detection&lt;/h4&gt;
&lt;p&gt;I am not aware of any reliable techniques to detect exploitation.
This is because no special system calls or privileges are required.&lt;/p&gt;
&lt;p&gt;It is definitely &lt;em&gt;not&lt;/em&gt; possible to detect improper usage of
&lt;code&gt;vzeroupper&lt;/code&gt; statically, please don’t try!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It turns out that memory management is hard, even in silicon 🙂&lt;/p&gt;
&lt;section id=&quot;acknowledgements&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Acknowledgements&lt;/h4&gt;
&lt;p&gt;This bug was discovered by me, Tavis Ormandy from Google Information
Security!&lt;/p&gt;
&lt;p&gt;I couldn’t have found it without help from my colleagues, in
particular Eduardo Vela Nava and Alexandra Sandulescu. I also had help
analyzing the bug from Josh Eads.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;aside id=&quot;footnotes&quot; class=&quot;footnotes footnotes-end-of-document&quot; role=&quot;doc-endnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;&lt;p&gt;You don’t need to set &lt;code&gt;ymm0&lt;/code&gt; explicitly, all
VEX encoded instructions that write to &lt;code&gt;xmm&lt;/code&gt; automatically
zero the upper part.&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fnref1&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&quot;fn2&quot;&gt;&lt;p&gt;See Software Optimization Guide for AMD EPYC™ 7003
Processors, section 2.11.5.&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fnref2&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&quot;fn3&quot;&gt;&lt;p&gt;See Software Optimization Guide for AMD EPYC™ 7003
Processors, section 2.11.5.&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fnref3&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&quot;fn4&quot;&gt;&lt;p&gt;In fact, conditional branches are not necessary at all
because of &lt;a href=&quot;https://grsecurity.net/amd_branch_mispredictor_part_2_where_no_cpu_has_gone_before&quot;&gt;SLS&lt;/a&gt;.&lt;a href=&quot;https://lock.cmpxchg8b.com/zenbleed.html#fnref4&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/aside&gt;



</description><link>https://lock.cmpxchg8b.com/zenbleed.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/zenbleed.html</guid><author>Tavis Ormandy</author></item><item><title>George Lucas once sued someone over a spreadsheet.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Templates of Doom!&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;introduction&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;In the late 1980s there was - believe it or not - a popular monthly
magazine dedicated to spreadsheets. It had a respectable eight year run,
along with two companion books.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-magazine.png&quot; alt=&quot;Lotus Magazines&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus Magazines&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The entire &lt;a href=&quot;https://archive.org/details/pub_lotus&quot;&gt;back
catalog&lt;/a&gt; is on archive.org, although most issues were scanned from
microfiche so there’s no color.&lt;/p&gt;
&lt;p&gt;I’m interested in Lotus 1-2-3 history, so I’ve been slowly reading
through back issues. It’s actually great, there are some really
approachable articles on linear regression, forecasting, calculating
bond yields, etc… mixed in with some retro computing news!&lt;/p&gt;
&lt;p&gt;There’s also advice, tips, reviews, reader questions, and so on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;why-1-2-3&quot;&gt;Why 1-2-3?&lt;/h3&gt;
&lt;p&gt;&lt;small&gt; I’m sometimes asked what it is I find appealing about Lotus
1-2-3. It’s from an era where there was an expectation that “powerful”
software would be designed around keystroke macros. I think I just
really appreciate that.&lt;/small&gt;&lt;/p&gt;&lt;small&gt;
&lt;p&gt;The moment that Vim first “clicked” for me is when I wrote my first
macro. I suddenly understood why there is this huge selection of
commands, and why that is so powerful.&lt;/p&gt;
&lt;/small&gt;&lt;p&gt;&lt;small&gt;In vim you can record a keystroke macro to a register called
&lt;code&gt;x&lt;/code&gt; with &lt;code&gt;qx&lt;/code&gt;, then run it with &lt;code&gt;@x&lt;/code&gt;.
In 1-2-3 you can write a keystroke macro to a range called
&lt;code&gt;\x&lt;/code&gt;, and run it with &lt;kbd&gt;Alt&lt;/kbd&gt;-&lt;kbd&gt;x&lt;/kbd&gt;. Not so
different! &lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;lotus&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Lotus&lt;/h1&gt;
&lt;p&gt;I was reading the December 1987 issue and read &lt;a href=&quot;https://archive.org/details/sim_lotus_1987-12_3_12/page/n147/mode/1up&quot;&gt;this
review&lt;/a&gt; of “Templates of Doom”, a game written in Lotus macros!
Hilarious, a real commercially released game you could actually buy for
$69.95! Of course, I had to try it.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/templates-review.jpg&quot; alt=&quot;Templates of Doom&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Templates of Doom&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As far as I can tell, the full game has not been archived anywhere,
but I was able to find a demo version from an old shareware archive. It
seems to work okay in the &lt;a href=&quot;https://github.com/taviso/123elf&quot;&gt;UNIX version&lt;/a&gt; of 1-2-3.&lt;/p&gt;
&lt;p&gt;I contacted the original author and asked him if he still had any old
copies. He did, he had some old 5.25” disks but didn’t have any hardware
to read them.&lt;/p&gt;
&lt;p&gt;Well, I happen to know an &lt;a href=&quot;https://scarybeastsecurity.blogspot.com/2021/05/recovering-lost-treasure-filled-floppy.html&quot;&gt;expert
in 5.25” disks&lt;/a&gt;, and with his help I was able to get a disk
image!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;history&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;History&lt;/h1&gt;
&lt;p&gt;The package I got in the mail looked like this, but there was this
page to stuck to the front that was peeling off.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/templatecover.gif&quot; alt=&quot;Cover Peeling&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Cover Peeling&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The glue had deterioated after 30 years, but there was clearly
something underneath…it was loose anyway, so I removed it revealing
different cover art.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/tdoom-secret-front.png&quot; style=&quot;width:45.0%&quot; alt=&quot;Secret Artwork&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/tdoom-front.png&quot; style=&quot;width:45.0%&quot; alt=&quot;Normal Artwork&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It turns out that in December 1988 &lt;a href=&quot;https://apnews.com/article/958f834f1ec23d92c83d5b0298b08a7e&quot;&gt;Lucasfilm
sued the author&lt;/a&gt; over similarities to “Indiana Jones and the Temple
of Doom”, and the author had to change the cover art to appease Lucas’s
lawyers!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;gameplay&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Gameplay&lt;/h1&gt;
&lt;p&gt;So how does the game work! It’s a set of 24 puzzles that encourage
you to learn how to use 1-2-3 features through problem solving.&lt;/p&gt;
&lt;p&gt;You’re timed when you start each puzzle, and scored based on how long
it took you to solve and how many hints you needed.&lt;/p&gt;
&lt;p&gt;It has a &lt;a href=&quot;https://en.wikipedia.org/wiki/Boss_key&quot;&gt;boss
key&lt;/a&gt; (&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;B&lt;/kbd&gt;) and a hint system
(&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;H&lt;/kbd&gt;). When you’ve solved the puzzle, you hit
&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;A&lt;/kbd&gt; and choose the right answer - if you win it
takes you to the score screen.&lt;/p&gt;
&lt;p&gt;If you want to see me solve one of the puzzles, there’s a &lt;a href=&quot;https://www.youtube.com/watch?v=XdZlLiMUsdI&quot;&gt;video&lt;/a&gt; here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;playing&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Playing&lt;/h1&gt;
&lt;p&gt;If you want to play this yourself…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install &lt;a href=&quot;https://github.com/taviso/123elf&quot;&gt;123elf&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Set your global directory to where the game is,
&lt;code&gt;/Worksheet Global Default Dir&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/File Retrieve STARTPRG.WKS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You might get the error
&lt;code&gt;File and/or extension converted&lt;/code&gt;. This is just 1-2-3
informing you it translated something from DOS to UNIX. Just hit
&lt;kbd&gt;Enter&lt;/kbd&gt;.&lt;/li&gt;
&lt;li&gt;When the file is loaded, press &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;S&lt;/kbd&gt; to
start.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;h4 id=&quot;note&quot;&gt;Note&lt;/h4&gt;
&lt;p&gt;You don’t have to, but the game assumes you’re using 80x25 (the
standard DOS resolution). Some cells you’re not supposed to see will be
visible if you don’t change your terminal to match that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now just follow the prompts!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remember to press &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;S&lt;/kbd&gt; when a new level
loads.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;archive&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Archive&lt;/h1&gt;
&lt;p&gt;I’ve uploaded a &lt;a href=&quot;https://archive.org/details/templatesdoom/&quot;&gt;disk image and a scan
of the manual&lt;/a&gt; to archive.org.&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/doom.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/doom.html</guid><author>Tavis Ormandy</author></item><item><title>I’m looking for a new wrist watch.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Quest for my perfect watch.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/prw-3100t.jpg&quot; alt=&quot;Casio PRW-3100T&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Casio PRW-3100T&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I love my Casio Protrek watch, I won’t leave the house without it.
I’ve owned it for years, as well as some earlier Protrek models. Those
earlier models didn’t break — these watches are basically indestructible
— I just liked them so much I went and bought the new revisions!&lt;/p&gt;
&lt;p&gt;It has the standard digital watch features (alarms, timers,
stopwatch, world time, calendar, backlight, etc), but this watch is
really intended for hikers — it has an altimeter, barometer, thermometer
and digital compass built in. I’m not outdoorsy — The only hiking I’ll
do is to Starbucks — but I still love this watch. I think the reason is
it’s reliable — I never worry that it won’t work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/prw-modes.png&quot; alt=&quot;Watch Modes&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;font size=&quot;-2&quot;&gt;
The standard watch modes displayed in the manual. &lt;br&gt; &lt;font size=&quot;-4&quot;&gt;
I think I’ve looked at the barometric pressure graph a total of two
times over the years. It is pretty cool though, the theory is you can
predict an upcoming weather change based on the direction the graph is
trending. If the graph is flat, the current weather is likely to hold
for a few hours. &lt;/font&gt; &lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Maybe you’re thinking “hah, unless the battery needs to be replaced”
— wrong! The watch face is literally a solar panel, it sips so little
power (we’re talking μamps) that a few cumulative minutes exposure to
daylight is enough to keep the watch running for months.&lt;/p&gt;
&lt;p&gt;I would be astonished if I woke up tomorrow and it wasn’t working,
and it’s rare I can say that about any other tech I use. If an
unattended update hosed my laptop, or an app update breaks my workflow —
oh well, that’s par for the course. Sure, a digital watch is simple in
comparison, but it’s nice to have something I can trust to do it’s
job.&lt;/p&gt;
&lt;section id=&quot;smartwatch&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Smartwatch&lt;/h2&gt;
&lt;p&gt;Still, I am a nerd — and I hear the siren call of smartwatches. I
think I would get frustrated quickly with an Android or iOS smartwatch,
but is there some middle ground? A simple, reliable, programmable
digital watch?&lt;/p&gt;
&lt;p&gt;It doesn’t have to be zero-maintenance, I can tolerate annual battery
replacements… but daily or weekly recharging and any dependence on
online services really seems unbearable to me after years with my
Casio.&lt;/p&gt;
&lt;p&gt;The main feature I would like is some sort of sync capability.
Ideally, I would like the alarms and &lt;a href=&quot;https://dianne.skoll.ca/projects/remind/&quot;&gt;reminders&lt;/a&gt; from my
UNIX &lt;code&gt;~/.reminders&lt;/code&gt; file to appear on my watch, perhaps over
RF or BLE. I don’t mind writing code to make this happen, so some
customizability, automation or programmability is essential.&lt;/p&gt;
&lt;p&gt;What options are there for someone like me?&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;go-homebrew&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Go Homebrew?&lt;/h2&gt;
&lt;p&gt;There are a few homebrew smartwatches that appeal to me.&lt;/p&gt;
&lt;section id=&quot;sensorwatch&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Sensorwatch&lt;/h3&gt;
&lt;p&gt;Jose Castillo has built &lt;a href=&quot;https://www.sensorwatch.net/&quot;&gt;SensorWatch&lt;/a&gt;, a fully
programmable replacement board for the Casio F-91W watch.&lt;/p&gt;
&lt;p&gt;This almost meets all my needs, it lasts a full year on a single
battery and I could write an app to handle my reminders. The only issue
is that sending data to the watch would be difficult once it’s
assembled, as only the buttons can be used for input.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/sensor-watch.jpg&quot; alt=&quot;Sensor Watch&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Sensor Watch&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There is an add-on board that adds GPIO pins, the idea is you can add
your own additional sensors… if you can make them fit. This might be
beyond my hardware skills, but perhaps exposing the pins externally to
add some form of i2c might be good enough — at the expense of water
resistance.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/sensor-watch-flexboard.jpg&quot; alt=&quot;Sensor Watch Addon&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Sensor Watch Addon&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I could ask Jose how he would add sync, perhaps he knows of some
small sensor like a magnetometer or IR photodiode that I could use to
build a low-bandwidth communication channel.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;goodwatch&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Goodwatch&lt;/h3&gt;
&lt;p&gt;Travis Goodspeed (unfortunately for him, people sometimes confuse us
because of our similar names!) has a very impressive project called &lt;a href=&quot;https://github.com/travisgoodspeed/goodwatch&quot;&gt;goodwatch&lt;/a&gt; for
amateur radio enthusiasts. It’s another board swap using the MSP430, but
for a different Casio shell with far more inputs and a more capable
segment display. Travis says a battery can last months, perhaps longer
depending on usage. That’s a little lower than I would like, but it
sounds tolerable.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/pocsag.jpg&quot; alt=&quot;Goodwatch&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Goodwatch&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I don’t have much amateur radio knowledge, but it seems plausible I
could add some low-bandwidth sync capability with a cheap
&lt;abbr title=&quot;Software Defined
Radio&quot;&gt;SDR&lt;/abbr&gt; dongle and a weekend hacking MSP340 assembly. I can
absolutely imagine using this, although Travis is a notable hardware
hacker and I can barely solder! Assembly looks a little more involved
than the sensorwatch, so I worry this might be out of my league!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;go-hybrid&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Go Hybrid?&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/sony-wena-pro.jpg&quot; alt=&quot;Sony Wena Pro&quot; referrerpolicy=&quot;no-referrer&quot;&gt; &lt;img src=&quot;https://lock.cmpxchg8b.com/img/wena3.jpg&quot; alt=&quot;Sony Wena3&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is an interesting Sony product line called Wena, primarily sold
in the Japanese market. Wena is not a smartwatch, it’s a “smartband”
that replaces the band on your existing watch. This seems moderately
interesting, it needs regular recharging but degrades gracefully into a
regular watch if the battery dies.&lt;/p&gt;
&lt;p&gt;Of course, there is no built-in functionality to use it for what I
want, but it seems totally plausible I could add it — decompiling the
companion APK if necessary to figure out the API.&lt;/p&gt;
&lt;p&gt;It is not cheap, and I would have to import one to the US. Frankly,
&lt;a href=&quot;https://www.wareable.com/smartwatches/sony-wena-wrist-pro-active-review-6982&quot;&gt;online
reviews&lt;/a&gt; are not favourable at all — but as I would write my own
software, I think I could make it work.&lt;/p&gt;
&lt;p&gt;There are two interesting models, the Wena Pro and the Wena3. The
Wena3 has a heartrate monitor, bigger screen and USB-C, but I think I
prefer the simpler Wena Pro design.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;go-retro&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Go Retro?&lt;/h2&gt;
&lt;p&gt;There have been smartwatches around since the 80s, and the
specifications I’m interested in are hardly cutting edge! Perhaps the
closest device to what I’m looking for is actually from 2003, the Timex
m851.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/m851.png&quot; alt=&quot;Timex m851&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Timex m851&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s a programmable digital watch based on the 8-bit SEIKO 88349 CPU,
and 2kb of RAM. Here is the &lt;a href=&quot;https://assets.timex.com/developer/developer_downloads/WA_Design_Guide.pdf&quot;&gt;developer
manual&lt;/a&gt;, and here is the &lt;a href=&quot;https://www.rayslogic.com/Software/TimexUSB/Docs/s1c88%20core%20cpu%20manual.pdf&quot;&gt;CPU
manual&lt;/a&gt;. Essentially, you can write “wrist apps” in S1C88 assembly,
and sync it over USB with a cable.&lt;/p&gt;
&lt;p&gt;There is apparently also a C compiler and simulator available,
although I suspect writing in C might not be an optimal use of the
constrained system resources!&lt;/p&gt;
&lt;p&gt;The simulator seems capable, here are screenshots from the
manual.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/timex-simulator.png&quot; alt=&quot;Timex Simulator&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Timex Simulator&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;The documentation says the battery needs to be replaced every 2
years, which is totally acceptable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/timex-battery.png&quot; alt=&quot;Timex Battery&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Timex Battery&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;There was also a modest enthusiast community at some point, who wrote
some interesting apps. Amusingly, this watch uses the same CPU as a
popular retro gaming device (the &lt;a href=&quot;https://wiki.sublab.net/index.php/Pokemon_Mini&quot;&gt;Pokemon mini&lt;/a&gt;),
so retro game hackers have already documented the toolchain and device
quirks, and even built emulators I might be able to repurpose!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Well, I’ve ordered a Wena Pro, a Sensor Watch and a (preowned) Timex
M851.&lt;/p&gt;
&lt;p&gt;I’ll try to get all three working and post a part two with my
developer review!&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/watch.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/watch.html</guid><author>Tavis Ormandy</author></item><item><title>Putting debugging symbols back into stripped binaries.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Unstripping Stripped Binaries&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;I’ve written before about how much I enjoy vintage software. Lately
I’ve been tinkering with &lt;a href=&quot;https://github.com/taviso/wpunix&quot;&gt;WordPerfect for UNIX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s working great, combined with &lt;a href=&quot;https://github.com/taviso/123elf/&quot;&gt;Lotus 1-2-3&lt;/a&gt; you can have a
full-featured office suite in an xterm! 😂&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/taviso/wpunix&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wp-preview.gif&quot; alt=&quot;WordPerfect&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;debugging&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Debugging&lt;/h1&gt;
&lt;p&gt;These are 30 year old stripped binaries that I’ve somehow managed to
patch into a working state. As you can imagine, when something doesn’t
work, tracking down what went wrong can be a real challenge.&lt;/p&gt;
&lt;p&gt;What I &lt;em&gt;really&lt;/em&gt; want is to take the names and types I’ve
figured out from my disassembler, and make them visible to gdb.&lt;/p&gt;
&lt;p&gt;I’ve found a simple solution to this that’s been working well for me,
here are some notes on it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;stabs&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Stabs&lt;/h1&gt;
&lt;p&gt;You’re probably familiar with &lt;a href=&quot;https://dwarfstd.org/&quot;&gt;DWARF&lt;/a&gt;, the debugging format used
everywhere in Linux. DWARF is really neat, it’s capable of expressing
the most complex locations and types possible.&lt;/p&gt;
&lt;p&gt;In this context, “location” means explaining to a debugger how to
find a variable. In some cases that’s really easy, but it gets
complicated fast. Perhaps the variable moves in and out of registers or
requires complex address calculation logic (e.g.&amp;nbsp;a bitfield struct
member stored in a stack frame).&lt;/p&gt;
&lt;p&gt;Figuring out these locations requires the debugger to execute little
bytecode programs called DWARF expressions – crazy stuff!&lt;/p&gt;
&lt;p&gt;Before DWARF, STABS (Symbol TABle Strings), was the predominant
debugging format. STABS takes simple locations and types, encodes them
into strings, and then stuffs them into the symbol table. It’s not very
elegant, but it worked.&lt;/p&gt;
&lt;p&gt;STABS is entirely obsolete, DWARF is superior in every way. However…
STABS does have one benefit that DWARF doesn’t – expressing simple stuff
(&lt;em&gt;very&lt;/em&gt; simple) is so easy you can even do it by hand…&lt;/p&gt;
&lt;section id=&quot;tools&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Tools&lt;/h2&gt;
&lt;p&gt;All the GNU tools still have native support for STABS. In fact, I’ve
noticed the GNU assembler even has pseudo-instructions that you can
write manually:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sourceware.org/binutils/docs/as/Stab.html&quot;&gt;https://sourceware.org/binutils/docs/as/Stab.html&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are three directives that begin ‘.stab’. All emit symbols (see
Symbols), for use by symbolic debuggers. Up to five fields are
required:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;string&lt;/strong&gt; &lt;br&gt; This is the symbol’s name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;type&lt;/strong&gt; &lt;br&gt; An absolute expression. The symbol’s type
is set to the low 8 bits of this expression. &lt;em&gt;Any bit pattern is
permitted, but ld and debuggers choke on silly bit patterns.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[…]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;value&lt;/strong&gt; &lt;br&gt; An absolute expression which becomes the
symbol’s value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you just want basic native types and simple locations, nothing
could be simpler than this.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;examples&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Examples&lt;/h1&gt;
&lt;p&gt;Let’s say I’ve figured out there is a function at
&lt;code&gt;0x8005bba&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb1-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt; example&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;*&lt;/span&gt;foo&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;long&lt;/span&gt; bar&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The stab to declare that is just this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.stabs &quot;example:f-11&quot;, N_FUN, 0, 0, 0x8005bba&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;f&lt;/code&gt; means this is a function, and &lt;code&gt;-11&lt;/code&gt; is
the pre-defined type for void. If you only want to use basic types, you
don’t even have to define them!&lt;/p&gt;
&lt;p&gt;Here is a list of some of the predefined types that GDB
recognizes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&quot;header&quot;&gt;
&lt;th&gt;Num&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;int, 32 bit signed integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;char, 8 bit type holding a character.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;td&gt;long, 32 bit signed integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-5&lt;/td&gt;
&lt;td&gt;unsigned char, 8 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-6&lt;/td&gt;
&lt;td&gt;signed char, 8 bit signed integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-7&lt;/td&gt;
&lt;td&gt;unsigned short, 16 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-8&lt;/td&gt;
&lt;td&gt;unsigned int, 32 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-9&lt;/td&gt;
&lt;td&gt;unsigned, 32 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-10&lt;/td&gt;
&lt;td&gt;unsigned long, 32 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-11&lt;/td&gt;
&lt;td&gt;void, type indicating the lack of a value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;-31&lt;/td&gt;
&lt;td&gt;long long, 64 bit signed integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;-32&lt;/td&gt;
&lt;td&gt;unsigned long long, 64 bit unsigned integral type.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are some confusing choices in there, but there are about 30
predefined types that GDB knows about.&lt;/p&gt;
&lt;p&gt;Notice I said “knows about”, that’s because there is no STABS
standard, just analyses of what crazy incompatible things all the 90s
UNIX vendors were doing!&lt;/p&gt;
&lt;p&gt;This cygnus document on stabs is great; it’s well written and
thorough, but occasional glimpses of frustration with Sun and IBM for
their incompatible undocumented extensions seep through.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sourceware.org/gdb/onlinedocs/stabs.pdf&quot;&gt;https://sourceware.org/gdb/onlinedocs/stabs.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;parameters&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Parameters&lt;/h1&gt;
&lt;p&gt;Okay, functions are working, what about function parameters? If
they’re one of the predefined types and this is a standard &lt;a href=&quot;https://en.wikipedia.org/wiki/Cdecl#cdecl&quot;&gt;cdecl&lt;/a&gt; function,
that’s easy too!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.stabs &quot;foo:p*-8&quot;, N_PSYM, 0, 0, 8
.stabs &quot;bar:p-10&quot;, N_PSYM, 0, 0, 12&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means there is a parameter &lt;code&gt;foo&lt;/code&gt;, a pointer to an
unsigned int at &lt;code&gt;bp+8&lt;/code&gt; and an unsigned long called
&lt;code&gt;bar&lt;/code&gt; at &lt;code&gt;bp+12&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I wrote some gas macros to make this less laborious, and now I can
just write this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function main, 0x8128000, %int
    param argc, %int
    param argv, ** %char
    param envp, ** %char&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pretty neat!&lt;/p&gt;
&lt;p&gt;You don’t even need to specify the offset – gas macros can store
counters between invocations, so I just keep incrementing it for each
new parameter, then reset it when you start a new function!&lt;/p&gt;
&lt;p&gt;They’re really simple, they look like this (some code ommitted):&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb5&quot;&gt;&lt;pre class=&quot;sourceCode as&quot;&gt;&lt;code class=&quot;sourceCode actionscript&quot;&gt;&lt;span id=&quot;cb5-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;macro&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;function&lt;/span&gt; name&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; address&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; ptr&lt;span class=&quot;op&quot;&gt;=,&lt;/span&gt; type&lt;/span&gt;
&lt;span id=&quot;cb5-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;set&lt;/span&gt; _arg&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;stabs&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;ame:f\ptr&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;ype&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; N_FUN&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; \address&lt;/span&gt;
&lt;span id=&quot;cb5-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;endm&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;macro&lt;/span&gt; param name&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; ptr&lt;span class=&quot;op&quot;&gt;=,&lt;/span&gt; type&lt;/span&gt;
&lt;span id=&quot;cb5-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;set&lt;/span&gt; _arg&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; _arg &lt;span class=&quot;op&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;stabs&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;ame:p\ptr&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;ype&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; N_PSYM&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; PARAM_SIZE&lt;span class=&quot;op&quot;&gt;+&lt;/span&gt;_arg&lt;span class=&quot;op&quot;&gt;*&lt;/span&gt;PARAM_SIZE&lt;/span&gt;
&lt;span id=&quot;cb5-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/symbols.html#cb5-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;endm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It works great, here is a sample gdb session. You can see I set
breakpoints, examine values, print types, and so on.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) add-symbol-file symbols.dbg
(gdb) pt rddec
type = boolean (char **, unsigned short *)
(gdb) x/i rddec
0x81dac90 &amp;lt;rddec&amp;gt;:   push   ebp
(gdb) b rddec
Breakpoint 1 at 0x81dac96
(gdb) c
Breakpoint 1, 0x081dac96 in rddec (numstr=0xffffc044, num=0x8350dbe)
(gdb) pt numstr
type = char **
(gdb) p *numstr
$1 = 0xffffc048 &quot;06&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even conditional breakpoints on parameter values work, it’s just like
unstripping the binary.&lt;/p&gt;
&lt;p&gt;In order to get a symbol file, I assemble them like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ as --32 -gstabs -o wp.o wp.s
$ as --32 -gstabs -o types.o types.s
$ ld -m elf_i386 -shared -Tdata=082d7938 -Ttext=0804a5f0 -Tbss=083377c0 -o wp.dbg wp.o types.o
$ strip --only-keep-debug wp.dbg&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s important to have the sections lined up with the target binary,
or gdb will get confused.&lt;/p&gt;
&lt;p&gt;Now you can just do this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(gdb) add-symbol-file wp.dbg
add symbol table from file &quot;wp.dbg&quot;
Reading symbols from wp.dbg...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I haven’t tried it, but I bet
&lt;code&gt;objcopy --add-gnu-debuglink&lt;/code&gt; would work too!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;usage&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Usage&lt;/h1&gt;
&lt;p&gt;I can write these symbols manually when I need to, but also wrote a
quick script to export these from my disassembler.&lt;/p&gt;
&lt;p&gt;The output is just thousands of lines like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function g_init, 0x0815EFA0, %int
function dflt_init, 0x0815F1A0, %int
function tool_init, 0x0815F620, %int
function g_close, 0x0815F630, %int
function g_inits, 0x0815F710, %int
function g_dint, 0x0815F720, %int
function g_dot, 0x0815F730, %int
function sub_8160290, 0x08160290, * %void&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Putting it all together, my stripped binary now has symbols and
parameter information in gdb, woohoo!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Breakpoint 2, 0x0815f1b9 in dflt_init ()
(gdb) bt
#0  0x0815f1b9 in dflt_init ()
#1  0x0815f02a in g_init ()
#2  0x0815acfc in int_dsp_xxx ()
#3  0x0814f8ad in gshow_init ()&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id=&quot;symbol-porting&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Symbol Porting&lt;/h1&gt;
&lt;p&gt;I actually have a huge advantage here that I didn’t mention.&lt;/p&gt;
&lt;p&gt;After a few of hours digging around on &lt;a href=&quot;https://archive.org/&quot;&gt;archive.org&lt;/a&gt;, I found a binary for an
older version of WordPerfect that wasn’t stripped! It must have been a
mistake while building the final &lt;abbr title=&quot;Release To
Manufacturing&quot;&gt;RTM&lt;/abbr&gt; binaries.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -l wp
-rw------- 1 taviso taviso 4.5M Jul 31  1996 wp
$ file wp
wp: ELF 32-bit MSB executable, SPARC, version 1 (SYSV), dynamically linked, interpreter /usr/lib/ld.so.1, not stripped&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately it’s not only an older version, but also for a
different architecture and operating system, Solaris SPARC. &lt;a href=&quot;https://www.zynamics.com/bindiff.html&quot;&gt;BinDiff&lt;/a&gt; does do pretty
well at matching these symbols to my i386 binary.&lt;/p&gt;
&lt;p&gt;I’ve found that people are sometimes surprised this works! Most of
the clever tricks BinDiff uses to match functions between two binaries
are actually architecture neutral. That means that If you have a
stripped binary for ARM and an unstripped version of the same binary for
MIPS, BinDiff can figure out which functions are which for you.&lt;/p&gt;
&lt;section id=&quot;string-references&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;String References&lt;/h2&gt;
&lt;p&gt;Some of the techniques used are easy to understand, like string
reference matching. If only a single function references the string
“error in function foo”, then it doesn’t matter if it’s SPARC or x86,
clearly this is the same function, so you just learned a symbol
name!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;cfg&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;CFG&lt;/h2&gt;
&lt;p&gt;It’s rare it’s that easy though, and BinDiff is full of insanely
clever tricks. Many of them involve
&lt;abbr title=&quot;Control Flow Graph&quot;&gt;CFG&lt;/abbr&gt; matching.&lt;/p&gt;
&lt;p&gt;If you generate a graph of all the function calls in two similar
binaries, there should be lots of matches.&lt;/p&gt;
&lt;p&gt;If function &lt;code&gt;foo&lt;/code&gt; calls function &lt;code&gt;bar&lt;/code&gt;, which
calls function &lt;code&gt;baz&lt;/code&gt; and &lt;code&gt;quux&lt;/code&gt;, then you don’t
need to know what architecture this is or what the functions do. You can
simply find the same unique graph and be confident these are the same
functions!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/graph_demo.png&quot; title=&quot;Trivial Example CFG&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/graph_demo.png&quot; alt=&quot;Control Flow Graph Matching&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;
DOT Code…
&lt;/summary&gt;
&lt;p&gt;
&lt;/p&gt;&lt;pre&gt;&lt;code&gt;digraph {
    rankdir=&quot;LR&quot;
    sub_123 -&amp;gt; sub_456
    sub_456 -&amp;gt; sub_789
    sub_456 -&amp;gt; sub_abc

    foo -&amp;gt; bar
    bar -&amp;gt; baz
    bar -&amp;gt; quux
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;This is just a trivial example, you can read about some more of the
BinDiff matching strategies &lt;a href=&quot;https://www.zynamics.com/bindiff/manual/#chapUnderstanding&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These tricks combined have let me debug and track down some pretty
gnarly issues!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;code&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Code&lt;/h1&gt;
&lt;p&gt;If the idea of using stabs to debug stripped code has piqued your
interest, you can take a look at my macros and code on github &lt;a href=&quot;https://github.com/taviso/wpunix/tree/main/debug&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully this could be a useful base for someone elses project!&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/symbols.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/symbols.html</guid><author>Tavis Ormandy</author></item><item><title>I solved a mystery in an old UNIX print ad.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;WordPerfect for UNIX&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;I found this interesting &lt;a href=&quot;https://archive.org/details/word-perfect-5.1-for-unix-ad-1993/&quot;&gt;print-ad&lt;/a&gt;
for WordPerfect for UNIX, apparently the best UNIX product of 1992!
(Very) Belated congratulations Corel!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/wp-unix1.png&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wp-unix1-small.png&quot; alt=&quot;WordPerfect Ad Pg1&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, this section intrigued me:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;But what if you don’t go for the GUI? Just choose the Character version, and
depending on your hardware, you’ll get great-looking features rarely seen on a
character terminal. Like document preview, column layout, and other important
features such as easy-to-use drop down menus and easy-to-understand help
screens.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I understand &lt;em&gt;“column layout”&lt;/em&gt; and &lt;em&gt;“drop down menus”&lt;/em&gt;,
but what exactly does &lt;em&gt;“document preview”&lt;/em&gt; mean?&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;mystery&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Mystery&lt;/h1&gt;
&lt;p&gt;Well, a weekend of hacking later and I have it working on modern
Linux!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wpscreenshot-linux.png&quot; alt=&quot;WordPerfect Linux&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;WordPerfect Linux&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer is it can generate Print Previews using &lt;a href=&quot;https://en.wikipedia.org/wiki/Sixel&quot;&gt;Sixels&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I find this hilarious.&lt;/p&gt;
&lt;p&gt;It requires a DEC VT340 compatible terminal (XTerm works fine). I
think it needs some tweaking to scale the image, but it basically
works!&lt;/p&gt;
&lt;p&gt;Here is a screenshot of the &lt;a href=&quot;https://wptoolbox.com/tips/RevealCodes.html&quot;&gt;Reveal Codes&lt;/a&gt; view
that WordPerfect users always talk about, and the generated preview!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wpscreenshot-layout.png&quot; alt=&quot;WordPerfect Linux&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;WordPerfect Linux&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wpscreenshot-sixels.png&quot; alt=&quot;WordPerfect Linux&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;WordPerfect Linux&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;It took a lot of tweaking to get it to work on modern Linux, I’ve put
my patches online &lt;a href=&quot;https://github.com/taviso/wpunix&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/wordperfect.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/wordperfect.html</guid><author>Tavis Ormandy</author></item><item><title>I rediscovered some lost UNIX software.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Lotus 1-2-3 For Linux&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-123-logo.png&quot; alt=&quot;Lotus 1-2-3 Logo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus 1-2-3 Logo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s an exciting time in the Lotus 1-2-3 enthusiast community – that
was a joke, there is no enthusiast community, it’s just me! 😆&lt;/p&gt;
&lt;p&gt;It really is an exciting time though – that part isn’t a joke!&lt;/p&gt;
&lt;p&gt;There have been some major developments in the last few weeks, and I
guess that’s pretty unusual for 30 year old abandonware.&lt;/p&gt;
&lt;p&gt;I’ll cut to the chase; through a combination of unlikely discoveries,
crazy hacks and the 90s BBS warez scene I’ve been able to port Lotus
1-2-3 natively to Linux – an operating system that literally didn’t
exist when 1-2-3 was released!&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#fn1&quot; class=&quot;footnote-ref&quot; id=&quot;fnref1&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to hear how a proprietary application could be ported to
new operating systems 30 years after release, read on.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;background&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Background&lt;/h1&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/123-sinewave.gif&quot; alt=&quot;Lotus 1-2-3 Demo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus 1-2-3 Demo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I really like Lotus 1-2-3, I even maintain a &lt;a href=&quot;https://github.com/taviso/lotusdrv&quot;&gt;driver&lt;/a&gt; to make sure it
works well on modern systems. I had to reverse engineer the driver api
to make that happen, but it works beautifully.&lt;/p&gt;
&lt;p&gt;Getting that driver working was quite an &lt;a href=&quot;https://lock.cmpxchg8b.com/lotus123.html&quot;&gt;adventure&lt;/a&gt;, but there is still one piece of the
puzzle missing: add-ins. 1-2-3 was designed to be extensible with
plugins (or “add-ins”) – in theory you could add support for modern
spreadsheet functions or integrate with Google Finance or something!&lt;/p&gt;
&lt;p&gt;The problem is add-ins had to be written in a special language called
LPL, and unfortunately the compiler and SDK have been lost. This is not
really surprising, this was a niche product and Lotus didn’t just give
the SDK away – they charged $395 for it. I can’t imagine many people
have a copy just lying around.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://books.google.com/books?id=_OEk1QOJYw0C&amp;amp;lpg=PT115&amp;amp;vq=toolkit&amp;amp;pg=PT115#v=onepage&amp;amp;q&amp;amp;f=false&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/pc-mag-toolkit.png&quot; alt=&quot;Lotus Toolkit Review&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There were lots of commercial plugins available in the heyday, some
were really &lt;a href=&quot;https://lock.cmpxchg8b.com/files/pcmag-apr1991-perfect-spreadsheet.pdf&quot;&gt;impressive&lt;/a&gt;. I
emailed a few developers who worked on some to see if they had any old
backups, the answer was always the same – 1-2-3 was the biggest name in
software, nobody thought it was going anywhere, so why would they keep
backups?&lt;/p&gt;
&lt;p&gt;There were also two third-party books written about LPL, I managed to
track them down from old libraries. I like the cover on this one, very
serious business. It’s so frustating, I can see screenshots of the
debugger, compiler output and sample add-ins - but just can’t do
anything with it without the SDK!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-badge-book.jpg&quot; alt=&quot;Lotus Toolkit Guide&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus Toolkit Guide&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;section id=&quot;progress&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Progress&lt;/h2&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-nfo-scenelist.png&quot; alt=&quot;Scenelist NFO&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Scenelist NFO&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Fast forward a year or two, and I found someone who used to be a
sysop in the ’90s BBS scene. Check out his &lt;a href=&quot;https://scenelist.org/&quot;&gt;website&lt;/a&gt;, he still has a catalogue of
NFOs and a telnet BBS online if you want to see some rad ANSI art!&lt;/p&gt;
&lt;p&gt;He had kept tape backups from old BBS systems, and was able to
recover a warez copy of the SDK – unbelievable! It actually worked, I
was able to build a few sample plugins!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lpl-compiler.jpg&quot; alt=&quot;LPL Compiler&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;LPL Compiler&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can download the ADK right &lt;a href=&quot;https://lock.cmpxchg8b.com/files/123DADK.ZIP&quot;&gt;here&lt;/a&gt;,
and here is a &lt;a href=&quot;https://github.com/taviso/lotusdrv/blob/master/addin/modern.pl&quot;&gt;sample&lt;/a&gt;
LPL program.&lt;/p&gt;
&lt;p&gt;This would already have been enough to keep me entertained for a
while, but it doesn’t stop there. It turns out that the BBS also had a
warez copy of Lotus 1-2-3 for UNIX. This was widely thought to be lost –
I’m told it couldn’t compete with a more popular UNIX office suite
called SCO Professional, so there were not many copies sold.&lt;/p&gt;
&lt;p&gt;I didn’t really have any use for it, but I’m definitely curious
enough to poke around on the installation media to see what’s in it!&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-unix-winrar.png&quot; alt=&quot;Lotus 1-2-3 For UNIX&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus 1-2-3 For UNIX&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;lotus-1-2-3-for-unix&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Lotus 1-2-3 for UNIX&lt;/h1&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/tdtitle.png&quot; alt=&quot;Teledisk Logo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Teledisk Logo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The warez release was a bunch of TD0 files, that’s a format I’ve
never seen before – apparently it’s an old compressed disk image format
from the 80s. I found &lt;a href=&quot;https://www.worldofsam.org/products/teledisk&quot;&gt;this page&lt;/a&gt; which
recommends using &lt;a href=&quot;https://github.com/simonowen/samdisk&quot;&gt;samdisk&lt;/a&gt; to convert it to
a raw disk image.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls
123UNIX1.TD0  123UNIX2.TD0  123UNIX3.TD0  123UNIX4.TD0  123UNIX5.TD0  LEGAL.NFO  WHATITIS
$ file *.TD0
123UNIX1.TD0: floppy image data (TeleDisk)
123UNIX2.TD0: floppy image data (TeleDisk)
123UNIX3.TD0: floppy image data (TeleDisk)
123UNIX4.TD0: floppy image data (TeleDisk)
123UNIX5.TD0: floppy image data (TeleDisk)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That seemed promising, &lt;code&gt;samdisk&lt;/code&gt; builds and seems to work!
I’ve uploaded the full &lt;a href=&quot;https://archive.org/details/123-unix&quot;&gt;images&lt;/a&gt; onto the Internet
Archive in case anyone else is curious enough to take a look.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ samdisk info 123UNIX1.TD0
[123UNIX1.TD0]
 Type:   TD0
 Size:   80 Cyls, 2 Heads

 created : 1991-06-22 20:24:04
Lotus 1-2-3 for UNIX System V
$ for i in *.TD0; do samdisk copy ${i} ${i/.TD0/.RAW}; done
Wrote 80 cyls, 2 heads, 18 sectors,  512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors,  512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors,  512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors,  512 bytes/sector = 1474560 bytes
Wrote 80 cyls, 2 heads, 18 sectors,  512 bytes/sector = 1474560 bytes&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ file *.RAW
123UNIX1.RAW: tar archive
123UNIX2.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX3.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX4.RAW: ASCII cpio archive (pre-SVR4 or odc)
123UNIX5.RAW: ASCII cpio archive (pre-SVR4 or odc)&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/123-unix-man.png&quot; alt=&quot;123 UNIX Manual&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;123 UNIX Manual&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I know that UNIX software was usually distributed as raw archives,
and you were expected to insert the diskette and run something like
&lt;code&gt;tar -C / -xvf /dev/fd0&lt;/code&gt;, so these files look right. I think
this is smart, why waste precious bytes on a filesystem?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ tar xf 123UNIX1.RAW
$ for i in 123UNIX{2..5}.RAW; do cpio -id &amp;lt; $i; done
1555 blocks
2606 blocks
2510 blocks
2481 blocks&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;
Full Directory Listing
&lt;/summary&gt;
&lt;p&gt;
&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ tree --prune -Dh
.
├── [ 512 May 20 13:53]  lotus
│&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  123.v10
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  cbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.4K May 20 13:53]  dfbcp437.bun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.4K May 20 13:53]  dfbcp850.bun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.0K May 20 13:53]  l13cUSA3.cbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.0K May 20 13:53]  l13cUSF3.cbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.0K May 20 13:53]  l13cUSL3.cbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.6K May 20 13:53]  l_ascii.bun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K May 20 13:53]  l_latin1.bun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 874 May 20 13:53]  l_lrf.bun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  fonts
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.6K Sep 20  1990]  aa012laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.3K Sep 20  1990]  aa012lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.4K Sep 20  1990]  aa012lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.9K Sep 20  1990]  aa017laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.8K Sep 20  1990]  aa017lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.8K Sep 20  1990]  aa024lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  aa024lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.8K Sep 20  1990]  aa025laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  aa030laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.6K Sep 20  1990]  aa030lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K Sep 20  1990]  aa033laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 18K Sep 20  1990]  aa033lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 22K Sep 20  1990]  aa050laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 29K Sep 20  1990]  aa060laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 15K Sep 20  1990]  aa060lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 53K Sep 20  1990]  aa100laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.4K Sep 20  1990]  ac016lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.1K Sep 20  1990]  ac016lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.1K Sep 20  1990]  ac017laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.1K Sep 20  1990]  ac022laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  ac022lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 13K Sep 20  1990]  ac033laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 16K Sep 20  1990]  ac040laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [9.4K Sep 20  1990]  ac040lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 34K Sep 20  1990]  ac066laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.7K Sep 20  1990]  ai012laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.3K Sep 20  1990]  ai012lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.5K Sep 20  1990]  ai012lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.1K Sep 20  1990]  ai017laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.0K Sep 20  1990]  ai017lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.9K Sep 20  1990]  ai024lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  ai024lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [9.0K Sep 20  1990]  ai025laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  ai030laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.5K Sep 20  1990]  ai030lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K Sep 20  1990]  ai033laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 19K Sep 20  1990]  ai033lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 23K Sep 20  1990]  ai050laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 29K Sep 20  1990]  ai060laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 15K Sep 20  1990]  ai060lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 55K Sep 20  1990]  ai100laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.5K Sep 20  1990]  aj016lfa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.1K Sep 20  1990]  aj016lha.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.2K Sep 20  1990]  aj017laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.1K Sep 20  1990]  aj022laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K Sep 20  1990]  aj022lia.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 13K Sep 20  1990]  aj033laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 17K Sep 20  1990]  aj040laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [9.4K Sep 20  1990]  aj040lga.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 35K Sep 20  1990]  aj066laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.6K Sep 20  1990]  bc008lba.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.0K Sep 20  1990]  bc010lba.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.3K Sep 20  1990]  bc012lea.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.5K Sep 20  1990]  bc014lda.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.8K Sep 20  1990]  bc016lba.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.8K Sep 20  1990]  bc016lca.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.7K Sep 20  1990]  bc016lda.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.1K Sep 20  1990]  bc018lba.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.8K Sep 20  1990]  bc021laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K Sep 20  1990]  bc031lca.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K Sep 20  1990]  bc031lda.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 14K Sep 20  1990]  bc042laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.7K Sep 20  1990]  bo008lda.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K Sep 20  1990]  bo014lca.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K Sep 20  1990]  bo014lda.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.1K Sep 20  1990]  bo016lba.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [3.5K Sep 20  1990]  bo019laa.lrf.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  hlp
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  USA-English
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [407K May 20 13:53]  123_sysV.hlp.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [7.3K May 20 13:53]  keyedit.hlp.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [8.2K May 20 13:53]  setup123.hlp.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  keymaps
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  a
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [3.7K May 20 13:53]  AT386.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  p
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [2.7K May 20 13:53]  PC-NFS.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  s
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K May 20 13:53]  sco386-101.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.7K May 20 13:53]  sun.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.7K May 20 13:53]  sysV386-101.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [5.7K May 20 13:53]  sysV386-xterm
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  v
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.7K May 20 13:53]  vt100.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 10K May 20 13:53]  vtxxx.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  w
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.2K May 20 13:53]  wyse-pce.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 14K May 20 13:53]  wyse50-lts123
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [5.9K May 20 13:53]  wyse50.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  pbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  egas25ca.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  egas25ci.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 605 Sep 20  1990]  egas25cs.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 602 Sep 20  1990]  egas25la.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 602 Sep 20  1990]  egas25li.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  egas25ls.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 574 Sep 20  1990]  egas25ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 574 Sep 20  1990]  egas25mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 576 Sep 20  1990]  egas25ms.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 592 Sep 20  1990]  egas25pa.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 592 Sep 20  1990]  egas25pi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 593 Sep 20  1990]  egas25ps.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  egas43ca.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  egas43ci.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 605 Sep 20  1990]  egas43cs.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 574 Sep 20  1990]  egas43ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 574 Sep 20  1990]  egas43mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 575 Sep 20  1990]  egas43ms.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 588 Sep 20  1990]  egas43pa.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 588 Sep 20  1990]  egas43pi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 589 Sep 20  1990]  egas43ps.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.4K Sep 20  1990]  epdtfx.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.4K Sep 20  1990]  epdtmx.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.7K Sep 20  1990]  eplql15p.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.3K Sep 20  1990]  eplql25m.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.2K Sep 20  1990]  eplql25p.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.4K Sep 20  1990]  eplql2mc.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.3K Sep 20  1990]  eplql2pc.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.9K Sep 20  1990]  eplql80m.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.6K Sep 20  1990]  eplql80p.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1020 Sep 20  1990]  gc_bs.pbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 754 Sep 20  1990]  gc_nobs.pbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.9K Sep 20  1990]  hplj1flo.pbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K Sep 20  1990]  hplj1jlo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K Sep 20  1990]  hplj1nol.pbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.2K Sep 20  1990]  hplj1zlo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.3K Sep 20  1990]  hplj2fhi.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.4K Sep 20  1990]  hplj2flo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.5K Sep 20  1990]  hplj2jhi.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.6K Sep 20  1990]  hplj2jlo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.9K Sep 20  1990]  hplj2noh.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.0K Sep 20  1990]  hpljnolo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.3K Sep 20  1990]  hpljpfhi.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.4K Sep 20  1990]  hpljpflo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.7K Sep 20  1990]  hpljpjhi.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.9K Sep 20  1990]  hpljpjlo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.1K Sep 20  1990]  hpljpnoh.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.3K Sep 20  1990]  hpljpnol.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.3K Sep 20  1990]  hpljzhi.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [5.4K Sep 20  1990]  hpljzlo.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.9K Sep 20  1990]  hppjpapr.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.9K Sep 20  1990]  hppjtran.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 976 Sep 20  1990]  hrcf25ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 976 Sep 20  1990]  hrcf25mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 977 Sep 20  1990]  hrcf25ms.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1006 Sep 20  1990]  hrcs43ma.vbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1006 Sep 20  1990]  hrcs43mi.vbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1006 Sep 20  1990]  hrcs43ms.vbd
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.6K Sep 20  1990]  ps00alw0.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K Sep 20  1990]  ps00alwp.pbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  vgas25ca.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 604 Sep 20  1990]  vgas25ci.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 606 Sep 20  1990]  vgas25cs.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 574 Sep 20  1990]  vgas25ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 573 Sep 20  1990]  vgas25mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 576 Sep 20  1990]  vgas25ms.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 601 Sep 20  1990]  vgas34ca.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 601 Sep 20  1990]  vgas34ci.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 602 Sep 20  1990]  vgas34cs.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 575 Sep 20  1990]  vgas34ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 575 Sep 20  1990]  vgas34mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 575 Sep 20  1990]  vgas43ms.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 602 Sep 20  1990]  vgas60ca.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 602 Sep 20  1990]  vgas60ci.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 603 Sep 20  1990]  vgas60cs.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 575 Sep 20  1990]  vgas60ma.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 575 Sep 20  1990]  vgas60mi.vbd.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  ri
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  USA-English
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1001 May 20 13:53]  123.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 602 May 20 13:53]  dr123txt.ri
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.9K May 20 13:53]  inst_dl.ri
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [5.3K May 20 13:53]  keyedit.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.0K May 20 13:53]  l123smp3.ri
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 49K May 20 13:53]  l123txt3.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 844 May 20 13:53]  license.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [6.9K May 20 13:53]  printer.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.6K May 20 13:53]  prsetup.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [ 23K May 20 13:53]  setup123.ri.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  smpfiles
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.3K May 20 13:53]  ACCTG.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.5K May 20 13:53]  CONSOL.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.7K May 20 13:53]  DATA.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.2K May 20 13:53]  DBT13S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.2K May 20 13:53]  DBT14S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.3K May 20 13:53]  INC10S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.0K May 20 13:53]  INC11S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.1K May 20 13:53]  INC12S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.7K May 20 13:53]  INC16S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 619 May 20 13:53]  INC2S.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 965 May 20 13:53]  INC4S.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 712 May 20 13:53]  INC5S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K May 20 13:53]  INC6S.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.2K May 20 13:53]  INC7S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.4K May 20 13:53]  INC8S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.7K May 20 13:53]  INC9S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [3.8K May 20 13:53]  MAC17S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.0K May 20 13:53]  MFG.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K May 20 13:53]  SALES.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.7K May 20 13:53]  SAMPMACS.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.5K May 20 13:53]  SHOES.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.6K May 20 13:53]  SMPSCHED.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.3K May 20 13:53]  SUM1988S.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.5K May 20 13:53]  SUMMARY.WK3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.2K May 20 13:53]  TABLES.WK3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.4K May 20 13:53]  consale.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.3K May 20 13:53]  consale.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [6.8K May 20 13:53]  consaler.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.2K May 20 13:53]  income.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.7K May 20 13:53]  income2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.7K May 20 13:53]  income3.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [8.7K May 20 13:53]  income4.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [7.2K May 20 13:53]  incomer.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  intro
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 874 Sep 20  1990]  chicago.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K Sep 20  1990]  debugmac.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 886 Sep 20  1990]  houston.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 799 Sep 20  1990]  less1ex1.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 565 Sep 20  1990]  less1ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 581 Sep 20  1990]  less2ex1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 877 Sep 20  1990]  less2ex2.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [4.1K Sep 20  1990]  less3ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 706 Sep 20  1990]  less4ex1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 834 Sep 20  1990]  less4ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 798 Sep 20  1990]  less5ex1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 811 Sep 20  1990]  less5ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 931 Sep 20  1990]  less5ex3.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 690 Sep 20  1990]  less6ex1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 781 Sep 20  1990]  less6ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 839 Sep 20  1990]  less7ex1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 856 Sep 20  1990]  less7ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 896 Sep 20  1990]  less8ex2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.7K Sep 20  1990]  less9ex1.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K Sep 20  1990]  macros.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.9K Sep 20  1990]  menuexam.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 774 Sep 20  1990]  menumac.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 647 Sep 20  1990]  mis.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 939 Sep 20  1990]  mlibrary.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 890 Sep 20  1990]  nyc.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.8K Sep 20  1990]  parts.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 698 Sep 20  1990]  planning.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [2.0K Sep 20  1990]  q2graph.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.3K Sep 20  1990]  q2graph2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 690 Sep 20  1990]  q2inc.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 763 Sep 20  1990]  q2inc2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 770 Sep 20  1990]  q2mac.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.2K Sep 20  1990]  q2macro.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.3K Sep 20  1990]  q2price.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 674 Sep 20  1990]  research.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [1.9K Sep 20  1990]  salary.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 878 Sep 20  1990]  sanfran.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [2.0K Sep 20  1990]  supplier.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K May 20 13:53]  menu2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 12K May 20 13:53]  menu3.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── [ 11K May 20 13:53]  menu4.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  nf
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.4K Sep 20  1990]  acctg.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [5.3K Sep 20  1990]  actual.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 871 Sep 20  1990]  asset.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1023 Sep 20  1990]  bud91.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [5.0K Sep 20  1990]  budget.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.5K Sep 20  1990]  calcex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.9K Sep 20  1990]  consol.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.2K Sep 20  1990]  data.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [4.9K Sep 20  1990]  dataex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [4.8K Sep 20  1990]  datatab.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 948 Sep 20  1990]  dattim.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 677 Sep 20  1990]  dept_emp.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 690 Sep 20  1990]  deptexp.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 852 Sep 20  1990]  deptname.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [4.4K Sep 20  1990]  dfillex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [6.9K Sep 20  1990]  graphex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 588 Sep 20  1990]  hotel90.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.0K Sep 20  1990]  hotel_q1.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.0K Sep 20  1990]  hotel_q2.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.0K Sep 20  1990]  hotel_q3.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.0K Sep 20  1990]  hotel_q4.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.4K Sep 20  1990]  joinex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [2.5K Sep 20  1990]  macex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.4K Sep 20  1990]  macros.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.4K Sep 20  1990]  mfg.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.8K Sep 20  1990]  mis.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.8K Sep 20  1990]  planning.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [8.4K Sep 20  1990]  print.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [8.4K Sep 20  1990]  print_bw.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.3K Sep 20  1990]  printex.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.3K Sep 20  1990]  q2price.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.8K Sep 20  1990]  research.wk1.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.1K Sep 20  1990]  rev-1.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.1K Sep 20  1990]  rev-2.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.9K Sep 20  1990]  salary.wk3
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [1.3K Sep 20  1990]  sales.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [8.4K Sep 20  1990]  shoes.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [8.4K Sep 20  1990]  shoes_bw.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 842 Sep 20  1990]  summary.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.3K Sep 20  1990]  tables.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [3.5K Sep 20  1990]  temp.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 985 Sep 20  1990]  util.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [4.2K Sep 20  1990]  world.wk3.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  sysV386
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 512 May 20 13:53]  bin
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 924 May 20 13:53]  123.sh.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.1M May 20 13:53]  123_exe.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [8.6K May 20 13:53]  inst_dl.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 96K May 20 13:53]  keyedit.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 14K May 20 13:53]  l13pbanr.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.4K May 20 13:53]  l13pupif.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [9.5K May 20 13:53]  prsetup123.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; └── [ 95K May 20 13:53]  setup123_exe.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 512 May 20 13:53]  dvr
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; └── [ 97K May 20 13:53]  l13pepdt.dvr.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     ├── [ 512 May 20 13:53]  lib
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.1M May 20 13:53]  123.o.z_1
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [717K May 20 13:53]  123.o.z_2
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 913 May 20 13:53]  dl_init.o.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 76K May 20 13:53]  ld.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 14K May 20 13:53]  mkdlobj.z
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 649 May 20 13:53]  stub.o
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 328 May 20 13:53]  tail.o
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; └── [1.3K May 20 13:53]  wyse50-lts123
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [  56 May 20 13:53]  lotus.bcf
│&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  man
│&amp;nbsp;&amp;nbsp;     ├── [ 512 May 20 13:53]  cat.LOCAL
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [3.0K May 20 13:53]  123.1.z
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [3.1K May 20 13:53]  123.LOCAL.z
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 738 May 20 13:53]  inst_dl.1.z
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.2K May 20 13:53]  inst_dl.LOCAL
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [ 706 May 20 13:53]  keyedit.1.z
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.1K May 20 13:53]  keyedit.LOCAL
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; ├── [1.5K May 20 13:53]  setup123.1.z
│&amp;nbsp;&amp;nbsp;     │&amp;nbsp;&amp;nbsp; └── [2.7K May 20 13:53]  setup123.LOCAL
│&amp;nbsp;&amp;nbsp;     └── [ 512 May 20 13:53]  man1
│&amp;nbsp;&amp;nbsp;         ├── [2.6K May 20 13:53]  123.1.z
│&amp;nbsp;&amp;nbsp;         ├── [ 931 May 20 13:53]  inst_dl.1
│&amp;nbsp;&amp;nbsp;         ├── [ 946 May 20 13:53]  keyedit.1
│&amp;nbsp;&amp;nbsp;         └── [2.0K May 20 13:53]  setup123.1
├── [ 512 May 20 13:53]  tmp
│&amp;nbsp;&amp;nbsp; ├── [ 512 May 20 13:53]  _lbl
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  prd=123
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;     └── [ 512 May 20 13:53]  typ=n386
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;         └── [ 512 May 20 13:53]  rel=1.0
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp;             └── [   0 Sep 20  1990]  vol=01
│&amp;nbsp;&amp;nbsp; ├── [ 178 Sep 20  1990]  init.123
│&amp;nbsp;&amp;nbsp; └── [ 512 May 20 13:53]  perms
│&amp;nbsp;&amp;nbsp;     └── [ 985 Sep 20  1990]  123
└── [ 512 May 20 13:53]  usr
    └── [ 512 May 20 13:53]  tmp
        └── [ 512 May 20 13:53]  lotus_install
            └── [ 512 May 20 13:53]  123
                ├── [ 19K Sep 20  1990]  Install
                ├── [ 860 Sep 20  1990]  banner
                ├── [ 20K Sep 20  1990]  chkdskno
                ├── [ 25K Sep 20  1990]  combine_exe
                ├── [ 208 Sep 20  1990]  copyright.d
                ├── [ 167 Sep 20  1990]  filelist.1
                ├── [4.8K Sep 20  1990]  filelist.2
                ├── [ 351 Sep 20  1990]  filelist.3
                ├── [ 294 Sep 20  1990]  filelist.4
                ├── [ 257 Sep 20  1990]  filelist.5
                ├── [ 12K Sep 20  1990]  messages
                └── [ 25K Sep 20  1990]  mk_banner

35 directories, 338 files&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;/section&gt;
&lt;section id=&quot;a-mystery-file&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;A Mystery File&lt;/h1&gt;
&lt;p&gt;While poking around, this one directory caught my eye, what exactly
is this?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -l
total 2.0M
-rw-r--r-- 1 taviso taviso 1.2M May 20 13:53 123.o.z_1
-rw-r--r-- 1 taviso taviso 717K May 20 13:53 123.o.z_2
-rw------- 1 taviso taviso  913 May 20 13:53 dl_init.o.z
-rwx------ 1 taviso taviso  77K May 20 13:53 ld.z*
-rwx------ 1 taviso taviso  14K May 20 13:53 mkdlobj.z*
-rw------- 1 taviso taviso  649 May 20 13:53 stub.o
-rw------- 1 taviso taviso  328 May 20 13:53 tail.o
-rw-r--r-- 1 taviso taviso 1.4K May 20 13:53 wyse50-lts123&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;123.o&lt;/code&gt; file is huge, even compressed it had to be
split across two disks. Let’s take a closer look…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat 123.o.z_? | gzip -d &amp;gt; 123.o
$ file 123.o
123.o: Intel 80386 COFF object file, not stripped, 5 sections, symbol offset=0x1efbdc, 19755 symbols, optional header size 28&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yikes - it’s an original unstripped object file from 1-2-3. There are
nearly 20,000 symbols including private symbols and debug
information.&lt;/p&gt;
&lt;p&gt;Why would Lotus ship this? It’s so big it must have required them to
phyiscally ship an extra disk to every customer? Could it have been a
mistake, accidentally left on the final release image?&lt;/p&gt;
&lt;p&gt;I had so many questions, but I’m not old enough to have any
experience with SysV, so I asked the greybeards on &lt;a href=&quot;news:alt.folklore.computers&quot;&gt;alt.folklore.computers&lt;/a&gt; if they
had seen this before and why this might have happened.&lt;/p&gt;
&lt;p&gt;The answer was that this is probably deliberate -
&lt;code&gt;dlopen()&lt;/code&gt; was not widely available on UNIX in the early 90s,
so there was no easy way to load native plugins or extensions. To solve
this, vendors would ship a bunch of &lt;a href=&quot;https://sourceware.org/binutils/docs-2.38/ld/Options.html#:~:text=This%20is%20often%20called%20partial%20linking.&quot;&gt;partially
linked&lt;/a&gt; object files with a script to relink them with your
extensions – Clever!&lt;/p&gt;
&lt;section id=&quot;hacking&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Hacking&lt;/h2&gt;
&lt;p&gt;I can’t tell you how useful this discovery was – the debug
information answered so many questions I had about Lotus 1-2-3
internals! This was a direct source port from DOS, so it mostly worked
the same way but now I had debugging data. For example, I really wanted
to hook into the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rasterisation&quot;&gt;rasterizer&lt;/a&gt; in my
driver so that I could improve the appearance of graphs in the terminal…
but it was just too complex to understand without documentation.&lt;/p&gt;
&lt;p&gt;I now know that the rasterizer dynamically generates little bytecode
programs that are interpreted by the graphics engine. Now that I know
what the opcodes are I can &lt;a href=&quot;https://github.com/taviso/lotusdrv/blob/master/raster.c#L62&quot;&gt;disassemble&lt;/a&gt;
and change them in my driver to improve the output!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;gnu-objcopy&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;GNU objcopy&lt;/h2&gt;
&lt;p&gt;Okay… but there is one more big question, I know that
&lt;code&gt;objcopy&lt;/code&gt; can convert COFF object files to ELF, the format
used by Linux. It seems like a long shot, but is it possible I could
link this into a native Linux program? 🐧&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ objcopy -I coff-i386 -O elf32-i386 123.o 123elf.o
$ file 123elf.o
123elf.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hilariously, the first version of Linux hadn’t even been released
when this object file was compiled – but I think this is possible! If
you want to hear about the technical challenges, read on!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ objdump -p 123.o  | grep Date
Time/Date               Sat Sep  8 06:23:50 1990&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;porting-problems&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Porting Problems&lt;/h1&gt;
&lt;section id=&quot;system-calls&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;System Calls&lt;/h2&gt;
&lt;p&gt;The first problem is that Linux and UNIX do not use a compatible
system call interface. UNIX uses the lcall7 interface, so we need to
find those calls and fix them up. Here is how this object file calls
&lt;code&gt;open()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ objdump -M intel --disassemble=open 123elf.o

123elf.o:     file format elf32-i386


Disassembly of section .text:

000e20d4 &amp;lt;open&amp;gt;:
   e20d4:   b8 05 00 00 00          mov    eax,0x5
   e20d9:   9a 00 00 00 00 07 00    call   0x7:0x0
   e20e0:   0f 82 c6 01 00 00       jb     e22ac &amp;lt;_cerror&amp;gt;
   e20e6:   c3                      ret    
   e20e7:   90                      nop&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;call&lt;/code&gt; instruction is what’s known as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Callgate&quot;&gt;callgate&lt;/a&gt;, which isn’t
supported on Linux&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#fn2&quot; class=&quot;footnote-ref&quot; id=&quot;fnref2&quot; role=&quot;doc-noteref&quot;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;, it will just crash. I want to
remove this and route all calls through glibc instead. My first thought
was just to mark these symbols as undefined, and then let the linker fix
that up by importing a replacement symbol from glibc.&lt;/p&gt;
&lt;section id=&quot;relocations&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Relocations&lt;/h3&gt;
&lt;p&gt;Nothing is ever easy, it turns out that won’t work! If we try,
&lt;code&gt;objcopy&lt;/code&gt; will simply refuse:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ objcopy -I coff-i386 -O elf32-i386 --strip-symbol open 123.o 123elf.o
objcopy: not stripping symbol `open&#39; because it is named in a relocation&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is &lt;code&gt;objcopy&lt;/code&gt; trying to tell us here?&lt;/p&gt;
&lt;p&gt;This is a relocatable object file, which simply means it can be
loaded at any address and still work. That’s possible because it
contains all the necessary information – the relocations – to adjust
it.&lt;/p&gt;
&lt;p&gt;Relocations are really simple, the compiler just records the name of
the symbol and the references to it. Now the linker can just walk
through and patch each reference to point to the new location –
easy.&lt;/p&gt;
&lt;p&gt;So &lt;code&gt;objcopy&lt;/code&gt; is saying that you can’t remove this symbol,
because the linker won’t know what to patch in when it moves it. Fair
enough – &lt;strong&gt;but&lt;/strong&gt;, just because objcopy won’t do it doesn’t
mean it’s impossible! We &lt;em&gt;could&lt;/em&gt; just fix the relocations too,
right?&lt;/p&gt;
&lt;p&gt;I don’t know of any tool that can do that, but COFF is not a &lt;a href=&quot;http://www.delorie.com/djgpp/doc/coff/&quot;&gt;complicated format&lt;/a&gt; –
I’ll write one!&lt;/p&gt;
&lt;p&gt;Introducing &lt;a href=&quot;https://github.com/taviso/123elf/blob/main/coffsyrup.c&quot;&gt;coffsyrup&lt;/a&gt;,
a tiny little tool that will remove those pesky COFF symbols even if
objcopy refuses!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ coffsyrup 123.o 123new.o open
MATCH open
RELOC rel open @0x180fa ~0xc9ede
RELOC rel open @0x4c9a1 ~0x95637
RELOC rel open @0x4d348 ~0x94c90
RELOC rel open @0x4ec13 ~0x933c5
&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id=&quot;incompatible-functions&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Incompatible Functions&lt;/h3&gt;
&lt;p&gt;Now that we can reroute functions, we have to worry about
incompatible functions.&lt;/p&gt;
&lt;p&gt;Lots of standard UNIX functions are &lt;em&gt;source&lt;/em&gt; but not
&lt;em&gt;binary&lt;/em&gt; compatible, this is because nobody promises that
structures are the same size or layout across UNIX versions. The obvious
example is &lt;code&gt;struct stat&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, this code is likely to work on any UNIX-like system you
can compile it on:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb13&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb13-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#cb13-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;struct&lt;/span&gt; stat sb&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#cb13-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#cb13-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;stat&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;/etc/passwd&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&lt;/span&gt;sb&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#cb13-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    printf&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;Size: &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;%u\n&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; sb&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;st_size&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#cb13-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, the resulting object file is &lt;em&gt;not&lt;/em&gt; likely to work on
any other system. That’s because the size of &lt;code&gt;struct stat&lt;/code&gt;
and the offset of &lt;code&gt;st_size&lt;/code&gt; will be different – it will
probably just corrupt your stack and crash!&lt;/p&gt;
&lt;p&gt;Luckily there are not really that many functions like this in UNIX.
In fact, the number is small enough that I can probably write wrappers
to translate them. The important ones are &lt;code&gt;stat()&lt;/code&gt;,
&lt;code&gt;times()&lt;/code&gt;, &lt;code&gt;uname()&lt;/code&gt;, &lt;code&gt;fcntl()&lt;/code&gt;,
&lt;code&gt;ioctl()&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;All I have to do is rename those symbols with &lt;code&gt;objcopy&lt;/code&gt;,
then mark them undefined with &lt;code&gt;coffsyrup&lt;/code&gt;. Now I can write a
little wrapper that translates a Linux &lt;code&gt;struct stat&lt;/code&gt; to a
UNIX &lt;code&gt;struct stat&lt;/code&gt; and it should work!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;termios&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Termios&lt;/h3&gt;
&lt;p&gt;Well…I said “little” wrappers – but there are some big
incompatabilities in places. One big nightmare was &lt;a href=&quot;https://en.wikibooks.org/wiki/Serial_Programming/termios&quot;&gt;termios&lt;/a&gt;.
Go ahead and take a look at the &lt;code&gt;termios(3)&lt;/code&gt; man page, pretty
complex right? Well, everything here works differently in subtle,
incompatible, and difficult to debug ways on every UNIX system.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;licensing&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Licensing&lt;/h3&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/license-failed.png&quot; alt=&quot;License Failed&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;License Failed&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Incredibly, after a bunch of hacking it actually runs without
crashing!&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-box-front.jpg&quot; alt=&quot;Lotus 1-2-3 Box&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;Lotus 1-2-3 Box&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;…and refuses to work without a license, damn! Well, I am a legitimate
licensed 1-2-3 owner with a boxed copy of 1-2-3, and this is 32 year old
abandonware. I think &lt;a href=&quot;https://en.wikipedia.org/wiki/Mitch_Kapor&quot;&gt;Mitch Kapor&lt;/a&gt; will
forgive me for bypassing this check.&lt;/p&gt;
&lt;p&gt;I can see from breaking on &lt;code&gt;exit()&lt;/code&gt; that there is an
internal symbol called &lt;code&gt;lic_init()&lt;/code&gt; responsible for checking
for a valid license. I looked at the code in IDA, and figured out the
logic.&lt;/p&gt;
&lt;p&gt;It is simply looking for a file called &lt;code&gt;LICENSE.000&lt;/code&gt;,
which contains an expiry date, username and systemname. If that all
matches what the system reports, the check passes! 🏴‍☠️&lt;/p&gt;
&lt;blockquote&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/ida-license-check.png&quot; alt=&quot;License Check&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;License Check&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;result&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Result&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=hMUhIg9B_So&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/123-linux-video-thumbnail.jpg&quot; alt=&quot;Youtube Video&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s it, Lotus 1-2-3 has been ported to a new operating system.
There are a few kinks that need to be ironed out, and I need to port
over my terminal driver, but it is 100% usable. At the moment, the DOS
version running under emulation looks better - but this can be
fixed!&lt;/p&gt;
&lt;p&gt;I hope you enjoyed reading about this, in the highly unlikely event
you actually want to try it yourself – all of my code is on &lt;a href=&quot;https://github.com/taviso/123elf&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;aside id=&quot;footnotes&quot; class=&quot;footnotes footnotes-end-of-document&quot; role=&quot;doc-endnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn1&quot;&gt;&lt;p&gt;I’m specifically talking about the classic R3, there
were releases until 2002.&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#fnref1&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&quot;fn2&quot;&gt;&lt;p&gt;Linux did have lcall7 and lcall27 compatability support
at one point, but alas no more.&lt;a href=&quot;https://lock.cmpxchg8b.com/linux123.html#fnref2&quot; class=&quot;footnote-back&quot; role=&quot;doc-backlink&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/aside&gt;



</description><link>https://lock.cmpxchg8b.com/linux123.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/linux123.html</guid><author>Tavis Ormandy</author></item><item><title>I wrote down some thoughts about Password Managers.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Password Managers.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: fc2269c2b7523b7a939e3f444343abf5c27bd089 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;introduction&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p&gt;I’ve spent a lot of time trying to understand the attack surface of
popular password managers. I think I’ve spent more time analyzing them
than practically anybody else, and I think that qualifies me to have an
opinion!&lt;/p&gt;
&lt;p&gt;First, let’s get a few things out of the way. For some reason, few
subjects can get heated faster than passwords. Maybe politics and
religion, but that’s about it. It’s okay if you don’t like my
opinion.&lt;/p&gt;
&lt;p&gt;Second, everyone needs to be using unique passwords. You don’t have
to use a password manager to do that, whatever system works for you is
fine. If you want to use a notebook in a desk drawer, that’s totally
acceptable.&lt;/p&gt;
&lt;p&gt;Okay, let’s begin.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you just want some advice on what to use, skip to the &lt;a href=&quot;https://lock.cmpxchg8b.com/passmgrs.html#conclusion&quot;&gt;conclusion&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;problems&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Problems&lt;/h1&gt;
&lt;p&gt;Conceptually, what could be simpler than a password manager? It’s
just a trivial key-value store. In fact, the simplest implementations
are usually great. Good examples of simple and safe password managers
are &lt;a href=&quot;https://keepass.info/&quot;&gt;keepass&lt;/a&gt; and &lt;a href=&quot;https://www.keepassx.org/&quot;&gt;keepassx&lt;/a&gt;, or even &lt;a href=&quot;https://www.passwordstore.org/&quot;&gt;pass&lt;/a&gt; if you’re a nerd.&lt;/p&gt;
&lt;p&gt;Things start to go wrong when you want integration with other
applications, or when you want data synchronized by an untrusted
intermediary. There are safe ways to achieve this, but the allure of
recurring subscription fees has attracted businesses to this space with
varying degrees of competence. I’m generally skeptical of these online
subscription password managers, and that’s going to be the focus of the
rest of this article.&lt;/p&gt;
&lt;section id=&quot;bad-advice&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Bad Advice&lt;/h2&gt;
&lt;p&gt;I often say that “use a password manager” is bad advice. That’s
because it’s difficult to tell the difference between a competent
implementation and a naive one. The tech press can review usability and
onboarding experience, but can’t realistically evaluate any security
claims, so how do you propose users tell the difference? For that
reason, I think “use a password manager” is so vague that it’s
dangerous.&lt;/p&gt;
&lt;p&gt;A good analogy is telling someone with a headache to pop any pills
they find in the medicine cabinet. Maybe they’ll get lucky and find an
aspirin, or maybe they won’t and you’ll be making a call to poison
control.&lt;/p&gt;
&lt;p&gt;Advice on this topic needs to be specific. It’s better to recommend
implementations that are well designed, rather than general product
categories. This position is surprisingly contentious, many people argue
any password manager is acceptable and that I’m sowing fear by actually
evaluating vendor claims. I remain unconvinced.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;attack-surface&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Attack Surface&lt;/h1&gt;
&lt;p&gt;My primary area of interest is how remote attackers can interact with
your password manager.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m not interested in things like testing how resistant encrypted
blobs are to offline cracking. This might be a valid concern for some,
but in most cases if an attacker is in a position to access or tamper
with encrypted state, then you were in trouble whether you used a
password manager or not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are two common issues I run into, the first is that trusted
user interface elements are injected into potentially hostile websites.
The second is that different components ipc over web-accessible channels
(e.g.&amp;nbsp;WebSockets, postMessage, etc.) without adequate mutual
authentication.&lt;/p&gt;
&lt;p&gt;Lets discuss user interface elements first.&lt;/p&gt;
&lt;section id=&quot;hostile-environments&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Hostile Environments&lt;/h2&gt;
&lt;p&gt;Most online password managers use &lt;a href=&quot;https://developer.chrome.com/docs/extensions/mv3/content_scripts/&quot;&gt;content
scripts&lt;/a&gt;, javascript that is inserted into every website you visit.
It’s really easy to write content scripts, but really tough to make them
tamper resistant. That’s kind of a problem, because they’re going to be
hosted in hostile environments.&lt;/p&gt;
&lt;p&gt;A good example of the kind of subtle ways this can go wrong is a bug
like &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&quot;&gt;this&lt;/a&gt;.
How isolated worlds interact is complicated enough, but password
managers make matters even worse by blurring the distinction between
user interface and content.&lt;/p&gt;
&lt;section id=&quot;chrome-vs-content&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Chrome vs Content&lt;/h3&gt;
&lt;p&gt;There are two primary components that make up your browser interface,
the chrome (confusingly, the term has nothing to do with Google Chrome)
and the content area. The chrome contains things like the address bar,
tabs and back button. These components can be trusted, and websites
can’t interfere with them. Conversely, anything inside the content area
can be controlled by the website and therefore it can’t be trusted.&lt;/p&gt;
&lt;p&gt;Most password managers blur this distinction by drawing their UI in
the content area. There is just no way to do this safely, and it’s
trivial to make demos that show why.&lt;/p&gt;
&lt;section id=&quot;frames&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Frames&lt;/h4&gt;
&lt;p&gt;Okay, but it’s not unusual to have different parts of the content
area with different privileges, that’s basically how iframes work. That
doesn’t make it safe though, and you’re probably familiar with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options&quot;&gt;X-Frame-Options&lt;/a&gt;,
it’s a way for websites to opt-out of being framed. Most security
conscious websites will do exactly that, because it’s just too easy for
attackers to trick you into interacting with frames in unintended
ways.&lt;/p&gt;
&lt;p&gt;These are sometimes called redress attacks or clickjacking (groan, I
hate that term). There’s a thorough analysis of the attack in the &lt;a href=&quot;https://code.google.com/archive/p/browsersec/wikis/Part2.wiki#Arbitrary_page_mashups_(UI_redressing)&quot;&gt;Browser
Security Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If redress attacks are problematic enough to make everyone disable
framing, should the software tasked with protecting your passwords also
opt-out? That was a trick question, they can’t!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;a-brief-illustration&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;A Brief Illustration&lt;/h3&gt;
&lt;p&gt;As I’m writing this, I happened to see a commercial for a new
password manager called &lt;a href=&quot;https://nordpass.com/&quot;&gt;Nordpass&lt;/a&gt;.
Let’s use it as a testcase and take a look. All online password managers
work in a similar way, this isn’t specific to any one
implementation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;I’m deliberately trying to avoid finding
specific vulnerabilities so that I don’t have to start the tedious
formal disclosure process. This is just a general illustration of
weaknesses inherent in these designs.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After installing the extension I immediately see that it uses content
scripts to add interface elements to login forms.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/nordpass-1.png&quot; title=&quot;Screenshot of the Nordpass Interface&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/nordpass-1.png&quot; alt=&quot;Nordpass User Interface&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In general, websites can interact with that UI, and quick testing
confirms that is true here. It looks like I can style those elements
too, that means trivial redress is possible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/nordpass-3.png&quot; title=&quot;Testing UI Redress&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/nordpass-3.png&quot; alt=&quot;Nordpass User Interface&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This problem is pervasive among online password managers, you can
never be sure if you’re interacting with a website or your password
manager. Let’s make a quick demo.&lt;/p&gt;
&lt;p&gt;Here is a quick example, you can try it out &lt;a href=&quot;https://lock.cmpxchg8b.com/te7Uin7i/test.html&quot;&gt;yourself&lt;/a&gt; if you
have Nordpass installed, please excuse my terrible design skills.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/nordpass-redress.gif&quot; title=&quot;Testing UI Redress&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/nordpass-redress.gif&quot; alt=&quot;Nordpass UI Redress&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a fundamental and unfixable problem with designs like
this.&lt;/p&gt;
&lt;p&gt;I’ve found many real, exploitable vulnerabilities in the way these UI
elements work. Trivial ones like &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=1481&quot;&gt;this&lt;/a&gt;,
or &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=1209&quot;&gt;this&lt;/a&gt;.
Sometimes interaction isn’t even necessary.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;interprocess-communication&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Interprocess Communication&lt;/h1&gt;
&lt;p&gt;We’ve already established that one component of online password
managers must be injected into potentially hostile environments. How can
those components communicate with other components?&lt;/p&gt;
&lt;p&gt;One naive solution would be to just use XHR or WebSockets to a local
HTTP endpoint. This sounds appealing to developers, they’re the native
way to communicate on the web. The problem with this solution is it’s
very difficult to differentiate between your content script, and a
hostile script running on the same page but a different world.&lt;/p&gt;
&lt;p&gt;Essentially every implementation I’ve looked at has got this wrong,
resulting in critical game-over vulnerabilities. Some worst offenders
are bugs like &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=693&quot;&gt;this&lt;/a&gt;,
&lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=773&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Vendors come up with all kind of hacky solutions to this, often
involving inherently racy background scripts that try to verify a tabs
origin.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;sandboxes&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Sandboxes&lt;/h1&gt;
&lt;p&gt;Another gripe I have with online password managers is that they
render browser sandboxes less effective. Modern browsers use a sandbox
architecture to isolate components that can go wrong.&lt;/p&gt;
&lt;p&gt;The problem is that online password managers effectively inject
privileged components into these sandboxed processes with extensions.
The purpose of sandboxing is to isolate potentially compromised
components from each other, but if you stuff all your most valuable
secrets inside the sandbox - then what’s the point?&lt;/p&gt;
&lt;p&gt;I worry that people don’t understand the tradeoff they’re making
here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;vendor-claims&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Vendor Claims&lt;/h1&gt;
&lt;p&gt;Despite what your vendor says, if their network is compromised, the
attacker can read your passwords. Here are some selected marketing
claims from password manager vendors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;“No one apart from you, not even [us], has access to your
passwords.”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“[We keep] your information private, secure and hidden (even
from us).”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“Your data is secured in a way that only you can view and
manage it. [Our] employees can’t”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;etc, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These claims are all nonsense. An attacker (or malicious insider) in
control of the vendor’s network can change the code that is served to
your browser, and that code can obviously access your passwords. This
isn’t farfetched, altering the content of websites (i.e.&amp;nbsp;&lt;a href=&quot;http://www.zone-h.org/&quot;&gt;defacement&lt;/a&gt;) is so common that it’s
practically a sport.&lt;/p&gt;
&lt;p&gt;The reality is that you have to trust your vendor to maintain their
infrastructure and keep it safe. The existence of encryption (“bank
grade” or not) does not alter this.&lt;/p&gt;
&lt;p&gt;Perhaps you think this isn’t a big deal, you already trusted them
when you installed their software. Fine, but these claims are front and
center in all marketing, so vendors must believe their customers care
about it. I think these claims are bending the truth to assuage
legitimate concerns.&lt;/p&gt;
&lt;section id=&quot;more-claims&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;More Claims&lt;/h2&gt;
&lt;p&gt;It’s easy to poke holes in marketing fluff, but here are some other
fun ones I noticed from real password manager vendors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keystroke encryption protects everything you type from being
read by cybercriminals.&lt;/strong&gt; &lt;em&gt;Oh, okay.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Many of the .NET assemblies […] are obfuscated, so even
using a disassembler users are unable to view critical areas of
methods/functions/classes.&lt;/strong&gt; &lt;em&gt;Well, I certainly feel
safer.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;etc, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;If you want to use an online password manager, I would recommend
using the one already built into your browser. They provide the same
functionality, and can sidestep these fundamental problems with
extensions.&lt;/p&gt;
&lt;p&gt;I use Chrome, but the other major browsers like Edge or Firefox are
fine too. They can isolate their trusted UI from websites, they don’t
break the sandbox security model, they have world-class security teams,
and they couldn’t be easier to use.&lt;/p&gt;
&lt;p&gt;No doubt there will be many people reading this who don’t like this
advice. All I can say is I’ve heard all the arguments, and stand by my
conclusions.&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/passmgrs.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/passmgrs.html</guid><author>Tavis Ormandy</author></item><item><title>I wrote a display driver for an old DOS program.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Lotus 1-2-3&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-online-help.png&quot; title=&quot;Screenshot of 1-2-3&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-online-help.png&quot; alt=&quot;1-2-3 Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I tend to use a lot of retro software, partly because I’m a big nerd
but also because I just prefer to work in a terminal. There were a ton
of major commercial software products for MS-DOS that were highly
polished, and can still be used effectively in an xterm today.&lt;/p&gt;
&lt;p&gt;I do like using &lt;a href=&quot;https://www.youtube.com/watch?v=5cNJNKkCQ2E&quot;&gt;Lotus 1-2-3&lt;/a&gt;, I
even have a boxed copy of the last DOS version released, version 4!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;problem&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Problem&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-broken-display.png&quot; title=&quot;Screenshot of 1-2-3 with broken display&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-broken-display.png&quot; alt=&quot;1-2-3 Broken Display&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The truth is I’m cheating, it does work, but it only supports a few
standard text mode &lt;a href=&quot;https://en.wikipedia.org/wiki/VGA_text_mode#PC_common_text_modes&quot;&gt;resolutions&lt;/a&gt;.
If your terminal is not exactly 80 columns wide, it just makes a big
ugly mess on your screen.&lt;/p&gt;
&lt;p&gt;There’s a workaround, just type &lt;code&gt;stty cols 80&lt;/code&gt;, and it
will be confined to a portion of your terminal, looking a bit sad. There
is no way to display more columns, and maximizing your terminal will do
nothing.&lt;/p&gt;
&lt;section id=&quot;research&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Research&lt;/h2&gt;
&lt;p&gt;I wondered if other people still uses 123, and if they had found a
solution. I enjoy using it, but being limited to such a small work area
is a real nuisance.&lt;/p&gt;
&lt;p&gt;I spent an hour reading vintage computing forums and old USENET
posts.&lt;/p&gt;
&lt;p&gt;Unfortunately, the answer is that nobody has a solution to my
resolution problem, but I did find something interesting…&lt;/p&gt;
&lt;section id=&quot;endeavor&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Endeavor&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/epson-manual.png&quot; title=&quot;Drivers that can support more text mode resolutions!&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/epson-manual.png&quot; alt=&quot;Epson Manual&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I found &lt;a href=&quot;https://files.support.epson.com/pdf/en____/en____us.pdf&quot;&gt;this old
manual&lt;/a&gt; for a 1993 PC Workstation called the Epson Endeavor, which
claimed to be distributed with a utilities disk that enabled a 100x31
text mode in 1-2-3.&lt;/p&gt;
&lt;p&gt;How could such a thing be possible?&lt;/p&gt;
&lt;p&gt;I was able to find the drivers on an old ftp site. Sadly they didn’t
work, presumably they needed a real Cirrus Logic VGA card, but the fact
that these drivers exist at all made me curious. It seems plausible that
123 can internally generate an arbitrary sized canvas, and it simply
asks the display driver what resolution it wants?&lt;/p&gt;
&lt;p&gt;I know that &lt;a href=&quot;https://github.com/dosemu2/dosemu2&quot;&gt;dosemu&lt;/a&gt;
can simulate arbitrary text mode resolutions, and some DOS software is
able to use it. Is it possible that I could write a dosemu display
driver, and 123 would just work in a fullscreen xterm?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/epson_content.jpg&quot; title=&quot;Epson Endeavor, 486 workstation from 1993&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/epson_content.jpg&quot; alt=&quot;Epson Endeavor&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;reverse-engineering&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Reverse Engineering&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/hiew-lotus.png&quot; title=&quot;Analyzing Configuration Files with Hiew&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hiew-lotus.png&quot; alt=&quot;Analyzing Configuration Files&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The display driver API is unfortunately quite complicated, I think it
might take a while to figure out with just a disassembler and a
debugger. Further complicating matters, 123 uses a technique called
overlays (like software bank switching) which confuses the debugger I’m
using as it loses breakpoints whenever a overlay switch occurs!&lt;/p&gt;
&lt;p&gt;I figured that Lotus must have sent Cirrus Logic some documentation
to write the driver, perhaps that still exists somewhere and would save
me some time.&lt;/p&gt;
&lt;p&gt;I spent some time looking, but came up empty.&lt;/p&gt;
&lt;p&gt;I learned a lot about the Lotus add-in ecosystem, and how they
worked. I figured I would download all the drivers and add-ins I could
find, I’ll start reversing the simplest one, and move on until I know
enough to write my own.&lt;/p&gt;
&lt;p&gt;If you want to read about what commercial addins were available, &lt;a href=&quot;https://lock.cmpxchg8b.com/files/pcmag-apr1991-perfect-spreadsheet.pdf&quot;&gt;this&lt;/a&gt; article is a
fun read.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;archives&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Archives&lt;/h2&gt;
&lt;p&gt;Believe it or not, the old &lt;a href=&quot;ftp://ftp.support.lotus.com/pub/&quot;&gt;Lotus ftp site&lt;/a&gt; is still
online. I mirrored everything they had to my workstation and sifted
through it.&lt;/p&gt;
&lt;p&gt;Then I got a lucky break…&lt;/p&gt;
&lt;section id=&quot;history&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;History&lt;/h3&gt;
&lt;p&gt;Lotus 1-2-3 was ported to a bunch of systems, including OpenVMS,
Xenix, and even System/390. In 1991, Lotus released a version for SunOS4
on SPARC.&lt;/p&gt;
&lt;p&gt;A few years later, a Lotus engineer uploaded a patch for a file
management bug in the XALERT component that affected the 1.2 update.
Luckily, they forgot to remove the &lt;a href=&quot;https://en.wikipedia.org/wiki/Stabs&quot;&gt;STABS&lt;/a&gt; data, leaving
incredibly rich debugging data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef short int lmbcs_type;
typedef short int platform;
typedef short int memory_type;
struct envblk_ { /* size 68 id 9 */
  short int envsize; /* bitsize 16, bitpos 0 */
  short int pfid; /* bitsize 16, bitpos 16 */
  short int csid; /* bitsize 16, bitpos 32 */
  short int pad; /* bitsize 16, bitpos 48 */
  short int maps; /* bitsize 16, bitpos 64 */
  short int type; /* bitsize 16, bitpos 80 */
  mptr nulh; /* bitsize 32, bitpos 96 */
  long unsigned int maxsize; /* bitsize 32, bitpos 128 */
  unsigned char *applid; /* bitsize 32, bitpos 160 */
  lhdl gabinfo; /* bitsize 32, bitpos 192 */
  char *regfile; /* bitsize 32, bitpos 224 */
  mptr (*get) (/* unknown */); /* bitsize 32, bitpos 256 */
  short int (*free) (/* unknown */); /* bitsize 32, bitpos 288 */
  lhdl (*map) (/* unknown */); /* bitsize 32, bitpos 320 */
  void (*unmap) (/* unknown */); /* bitsize 32, bitpos 352 */
  lhdl (*alloc) (/* unknown */); /* bitsize 32, bitpos 384 */
  short int (*dealloc) (/* unknown */); /* bitsize 32, bitpos 416 */
  short unsigned int (*load) (/* unknown */); /* bitsize 32, bitpos 448 */
  short unsigned int (*unload) (/* unknown */); /* bitsize 32, bitpos 480 */
  short unsigned int (*syscall) (/* unknown */); /* bitsize 32, bitpos 512 */
};
typedef struct envblk_ /* id 9 */ envblk;
typedef long int LpiSTATUS;
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was the only file (trust me, I checked them all) that had any
debugging data left on the whole ftp!&lt;/p&gt;
&lt;p&gt;Now, this file was for a different architecture, operating system,
compiler, release and was a long obsolete format. &lt;em&gt;But&lt;/em&gt;, the
clues it contained about the driver API were invaluable, and saved me so
much time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ file alert.so1
alert.so1: SPARC demand paged shared library not stripped&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file was so old that to read the data I had to find an older
version of binutils that still supported &lt;code&gt;a.out-sunos-big&lt;/code&gt;.
Luckily that worked and it was able to reconstruct a ton of useful
data.&lt;/p&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://lock.cmpxchg8b.com/files/alert.txt&quot;&gt;full recovered data&lt;/a&gt;, if
you’re interested.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;more-luck&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;More Luck&lt;/h3&gt;
&lt;p&gt;I got another stroke of luck, I found a third party printer driver on
an old SUNET archive for the Siemens Highprint 7400. Remarkably, it had
some ancient Codeview debugging data left in it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/s-l1200.jpg&quot; title=&quot;Siemens Highprint 7400&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/s-l1200.jpg&quot; alt=&quot;Siemens Highprint 7400&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is a picture of a lady printing some rorschach tests with her
new Highprint 7400, for some reason.&lt;/p&gt;
&lt;p&gt;The debugging data was so old, I struggled to find a tool that could
parse it. The data had a version code &lt;code&gt;NB02&lt;/code&gt;, which means
they probably used &lt;code&gt;link.exe&lt;/code&gt; version 5, released around
1988. I tried TDUMP, CVDUMP and even SYMDEB, and none recognized it. I
did eventually find an old enough version of Codeview that could read
it, but it had no way to save output to a file except “printing” your
backlog, and the backlog wasn’t big enough to hold all the data!&lt;/p&gt;
&lt;p&gt;I had to script a DOS emulator to scrape the data out a page at a
time! I can’t tell you how much time I wasted getting this working, but
it was worth it, it gave some valuable hints about how the DEVPRIM
(Device Primitive) API worked.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Publics for: _DVTBL!*
Symbols for: L13PSI74!*
3CD5:016E struct           psd
3CD5:006E struct GdvEntryTbl1_ pcf
3CD5:01DC unsigned int     print_head_height_dots
3CD5:000A char             grph_init_string[]
3CD5:0158 char             gr_eol_str[]
3CD5:016C unsigned int     col_res_num_pos
3CD5:0020 char             start_grph_str[]
3CD5:0050 char             last_grph_lf_str[]
3CB1:001C unsigned char far * graph_data_ptr
3CD5:0064 char             gr_eog_str[]
3CD5:0006 unsigned long    graph_data_size
3CD5:0000 unsigned int     scan_width
3CD5:001E unsigned int     stripNum
3AC8:0006 unsigned int PROC  hmu_len()
[BP+0008] unsigned char far * lmbcs_str_buf
[BP+0006] unsigned int     lbuf_len
[BP-0006] unsigned int     return_hmu
[BP-000A] unsigned char far * bundle_ptr
[BP-0004] unsigned char far * orig_ptr
3AC8:0148 void PROC        finish_down()
[BP+0006] unsigned int     distance
3CB1:000C char             max_feed[]
[BP-0006] int              i
[BP-0004] unsigned char far * p
3AC8:01A4 int PROC         graphics_init_printer()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://lock.cmpxchg8b.com/files/L13PSI74.txt&quot;&gt;full recovered data&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;hello-world&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Hello World&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-ida.png&quot; title=&quot;Reversing a Lotus driver with IDA&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-ida.png&quot; alt=&quot;IDA Reversing Lotus&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With this data, and a weekend in IDA I was able to figure out a large
amount of the API. It was painful getting a toolchain that worked, but I
was eventually able to produce something that Lotus could load.&lt;/p&gt;
&lt;p&gt;I think I must be the first person to write a Lotus 1-2-3 driver in
thirty years!&lt;/p&gt;
&lt;p&gt;My driver didn’t actually do anything, but when I responded to device
information requests claiming to support 256 columns, I saw in my log
that it sent me an &lt;a href=&quot;https://en.wikipedia.org/wiki/Lmbcs&quot;&gt;LMBCS&lt;/a&gt; string to decode
like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ENTER WriteLmbcsStringWithAttributes, caller 15f:6c7d
lotdemu.c:291: TRACE byteslen 238
lotdemu.c:293: TRACE attrs 000
lmbcs.c:104: TRACE translate_lmbcs
lmbcs.c:106: TRACE maxdst 256
lmbcs.c:107: TRACE maxsrc 238
000000: 20 20 20 20 41 20 20 20 20 20 20 20 20 42 20 20      A        B
000010: 20 20 20 20 20 20 43 20 20 20 20 20 20 20 20 44        C        D
000020: 20 20 20 20 20 20 20 20 45 20 20 20 20 20 20 20          E
000030: 20 46 20 20 20 20 20 20 20 20 47 20 20 20 20 20   F        G
000040: 20 20 20 48 20 20 20 20 20 20 20 20 49 20 20 20     H        I
000050: 20 20 20 20 20 4a 20 20 20 20 20 20 20 20 4b 20       J        K
000060: 20 20 20 20 20 20 20 4c 20 20 20 20 20 20 20 20         L
000070: 4d 20 20 20 20 20 20 20 20 4e 20 20 20 20 20 20  M        N
000080: 20 20 4f 20 20 20 20 20 20 20 20 50 20 20 20 20    O        P
000090: 20 20 20 20 51 20 20 20 20 20 20 20 20 52 20 20      Q        R
0000a0: 20 20 20 20 20 20 53 20 20 20 20 20 20 20 20 54        S        T
0000b0: 20 20 20 20 20 20 20 20 55 20 20 20 20 20 20 20          U
0000c0: 20 56 20 20 20 20 20 20 20 20 57 20 20 20 20 20   V        W
0000d0: 20 20 20 58 20 20 20 20 20 20 20 20 59 20 20 20     X        Y
0000e0: 20 20 20 20 20 5a 20 20 20 20 20 20 20 20             Z
translated characters is 238
lotdemu.c:209: TRACE WriteStringToFramebuffer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s asking me to write 26 column headers to the framebuffer!&lt;/p&gt;
&lt;p&gt;This validated the idea, Lotus 1-2-3 does internally support
arbitrary resolutions!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;results&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Results&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/wide-dosemu.png&quot; title=&quot;So many columns...&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/wide-dosemu.png&quot; alt=&quot;So.. many.. columns..&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A ton of hacking later, and I do now have a usable driver for dosemu
that supports arbitrary resolutions, just look at all those columns!&lt;/p&gt;
&lt;p&gt;I know enough about the API that I think I can probably add &lt;a href=&quot;http://caca.zoy.org/wiki/libcaca&quot;&gt;libcaca&lt;/a&gt; support so that text
mode graphs work, but that’s a project for another weekend!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;My work-in-progress source code is available on &lt;a href=&quot;https://github.com/taviso/lotusdrv&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ve put all of my &lt;a href=&quot;https://lock.cmpxchg8b.com/lotusinternals.html&quot;&gt;notes&lt;/a&gt; online so
that in the event that anybody else wants to make 123 DOS work somewhere
new, they won’t have to repeat all my work!&lt;/p&gt;
&lt;section id=&quot;update&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;I got ascii art graphs working in a terminal too! Here is a 123
plotting a sine wave in an xterm.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/123-sinewave.gif&quot; title=&quot;Ascii art sine wave&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/123-sinewave.gif&quot; alt=&quot;Ascii art sine wave&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;/section&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/lotus123.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/lotus123.html</guid><author>Tavis Ormandy</author></item><item><title>You can make popup ads in XTerm with Tektronix mode.Linking a binary with two versions of the same library.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Tektronix Popups.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;xterm can emulate a &lt;a href=&quot;https://en.wikipedia.org/wiki/Tektronix_4010&quot;&gt;Tektronix 4010&lt;/a&gt;
terminal, It’s actually pretty neat, it’s enabled by default and
applications can plot simple graphics (lines, curves, etc) or use it to
show graphs.&lt;/p&gt;
&lt;p&gt;It randomly occurred to me that windows unexpectedly opening is a bit
like a popup browser ad.&lt;/p&gt;
&lt;p&gt;Well, it turns out that Tektronix emulation in xterm is so fast that
you can draw monochrome bitmaps. Nobody ever asked for or wanted this,
but here is a &lt;a href=&quot;https://lock.cmpxchg8b.com/files/README.xterm.txt&quot;&gt;README file&lt;/a&gt; that
abuses this to create a popup ad when you &lt;code&gt;cat&lt;/code&gt; it in an
xterm. 😈&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/xtermpopup.mp4&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/xtermpopup.gif&quot; alt=&quot;XTerm Popup Ad&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id=&quot;recipe&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Recipe&lt;/h1&gt;
&lt;p&gt;Here is how I made this:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;https://lock.cmpxchg8b.com/img/gimp-popup.png&quot; alt=&quot;GIMP popup-ad&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
&lt;figcaption aria-hidden=&quot;true&quot;&gt;GIMP popup-ad&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;First I created a simple popup ad in GIMP, and saved it as a 1bit
png image, then converted that to raw RGB with ImageMagick.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;$ convert -flip -colors 2 xterm-popup.png rgb:xterm-popup-400x200.rgb&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Then I wrote this quick C program to generate a plot.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#include &lt;/span&gt;&lt;span class=&quot;im&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;#include &lt;/span&gt;&lt;span class=&quot;im&quot;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// Convert RGB into monochrome points for gnuplot&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// Usage: ./genplot &amp;lt;width&amp;gt; &amp;lt;height&amp;gt; &amp;lt; image.rgb &amp;gt; plot.dat&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;// -- taviso@gmail.com December, 2020&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-8&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-8&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; main&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; argc&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;**&lt;/span&gt;argv&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-9&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-9&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-10&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-10&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; r&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; g&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-11&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-11&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; x&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-12&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-12&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; width  &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; atoi&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;argv&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-13&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-13&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dt&quot;&gt;int&lt;/span&gt; height &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; atoi&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;argv&lt;span class=&quot;op&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-14&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-14&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-15&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-15&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;;&lt;/span&gt; x&lt;span class=&quot;op&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-16&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-16&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-17&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-17&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        x &lt;span class=&quot;op&quot;&gt;%=&lt;/span&gt; width&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-18&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-18&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        y &lt;span class=&quot;op&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!&lt;/span&gt;x&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-19&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-19&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-20&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-20&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        r &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; getchar&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-21&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-21&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        g &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; getchar&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-22&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-22&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        b &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; getchar&lt;span class=&quot;op&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-23&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-23&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-24&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-24&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;feof&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;stdin&lt;span class=&quot;op&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-25&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-25&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-26&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-26&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-27&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-27&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(!!&lt;/span&gt;r &lt;span class=&quot;op&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!!&lt;/span&gt;g &lt;span class=&quot;op&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;!!&lt;/span&gt;b&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-28&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-28&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-29&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-29&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-30&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-30&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        printf&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;st&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;%u\n&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; y &lt;span class=&quot;op&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-31&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-31&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-32&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-32&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-33&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/tektronix.html#cb2-33&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now gnuplot can parse that file and generate Tetronix commands that
you can just append to a text file. I needed to experiment with the
&lt;code&gt;pt&lt;/code&gt; and &lt;code&gt;ps&lt;/code&gt; options to find the best look, but
it worked out pretty well. You’re welcome!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gnuplot -e &quot;set terminal xterm;
&amp;gt;  unset key; unset ytics; unset xtics;
&amp;gt;  plot &#39;plot.dat&#39; pt 2 ps 1&quot; &amp;gt;&amp;gt; README.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t worry, if this is ever abused, you can always do
&lt;code&gt;alias ucat-origin=&quot;cat -v&quot;&lt;/code&gt; 😂&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/tektronix.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/tektronix.html</guid><author>Tavis Ormandy</author></item><item><title>I reviewed some spreadsheets that work in the terminal.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Console Spreadsheets.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: f0cc50a6fc6a5dd652c2b96ca9c1779f763fd6b1 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;I’m not a heavy spreadsheet user, just keeping track of expenses,
household inventory, investments, that kind of thing. I mostly just use
Google Sheets.&lt;/p&gt;
&lt;p&gt;However, I really prefer to stay in a terminal if I can, so decided
to review some of the options. My only requirements are that it runs in
a terminal and works on Linux, but bonus points if it’s extensible
(scripts, macros, plugins, etc).&lt;/p&gt;
&lt;p&gt;I’ve tested two F/OSS and two commercial console spreadsheets. Here’s
the big caveat, it might surprise you to learn (haha) that there isn’t a
huge demand for console productivity software. That means these programs
havn’t been actively developed for a very long time, but they were once
&lt;strong&gt;incredibly&lt;/strong&gt; popular.&lt;/p&gt;
&lt;p&gt;Their userbase may now be extinct, but they’re all still mature,
stable and functional.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;spreadsheets&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Spreadsheets&lt;/h1&gt;
&lt;section id=&quot;spreadsheet-calculator-sc&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Spreadsheet Calculator (sc)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/sc-simple.png&quot; title=&quot;Screenshot of sc&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/sc-simple.png&quot; alt=&quot;sc Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Sc_(spreadsheet_calculator)&quot;&gt;sc&lt;/a&gt;
is the classic UNIX spreadsheet, written by James Gosling. It’s public
domain, and available in most linux distributions.&lt;/p&gt;
&lt;p&gt;Perhaps the most interesting thing about sc is that spreadsheets are
saved as sequences of commands, and a &lt;code&gt;.sc&lt;/code&gt; file is a human
readable script. Here’s an example, if I dump the saved file from the
screenshot above, you can see it looks like a scripting language:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat test.sc
# This data file was generated by the Spreadsheet Calculator.
# You almost certainly shouldn&#39;t edit it.

rightstring B0 = &quot;Cost&quot;
rightstring C0 = &quot;Count&quot;
rightstring D0 = &quot;Total&quot;
rightstring A1 = &quot;Screws&quot;
let B1 = 0.02
let C1 = 23
let D1 = B1*C1
rightstring A2 = &quot;Plates&quot;
let B2 = 0.44
let C2 = 8
let D2 = B2*C2
label D3 = &quot;------------&quot;
let D4 = @sum(D0:D2)
goto D6 A0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don’t have to enter these commands directly, as you move around
and press keybindings, sc populates the command entry for you - you
simply add the options and parameters. I found a particularly good
introduction to using sc &lt;a href=&quot;http://blog.startaylor.net/2016/08/29/sc/&quot;&gt;here&lt;/a&gt;. It turns out
sc doesn’t have any way to import data, so I had to write a quick script
to do that - but once you do get data into sc, all the standard
spreadsheet features are there.&lt;/p&gt;
&lt;p&gt;The syntax for formulas, cell references and ranges is all familiar.
When working with ranges, you can select them in an interactive visual
mode and then name them for use in formulas.&lt;/p&gt;
&lt;p&gt;That said, while formula syntax, cell references/ranges
(e.g.&amp;nbsp;&lt;code&gt;C3&lt;/code&gt;, &lt;code&gt;$A$1:B2&lt;/code&gt;, etc) and basic navigation
are all familiar, the list ends there.&lt;/p&gt;
&lt;p&gt;You’re going to have to read the documentation to do anything more
advanced than editing cells. The commands mostly make sense, but you’re
not prompted for any parameters, you have to memorize the options you
need from the documentation before you enter command editing mode. Good
luck getting that right.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/sc-sort-range.gif&quot; title=&quot;Demonstration of selecting and sorting a table in sc (color enabled)&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/sc-sort-range.gif&quot; alt=&quot;sc Sort Demo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;sc does support macros, so you can create a macro for commonly used
options (a macro is essentially just a sc command script), and even bind
it to a function key.&lt;/p&gt;
&lt;p&gt;You can’t rebind standard movement or navigation keys, but most
functionality is accessed via commands, and you can define your own.&lt;/p&gt;
&lt;section id=&quot;summary&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 18%&quot;&gt;
&lt;col style=&quot;width: 81%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Public Domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;Portable, C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Last Release&lt;/td&gt;
&lt;td&gt;2002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td&gt;None, probably &lt;a href=&quot;https://groups.google.com/g/comp.sources.unix/&quot;&gt;comp.sources.unix&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id=&quot;pros&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Portable, Public Domain.&lt;/li&gt;
&lt;li&gt;Macro support.&lt;/li&gt;
&lt;li&gt;Most standard spreadsheet features and functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id=&quot;cons&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Cons&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Steep learning curve.&lt;/li&gt;
&lt;li&gt;No graphs, although you could pipe data into gnuplot.&lt;/li&gt;
&lt;li&gt;No undo.&lt;/li&gt;
&lt;li&gt;No import.&lt;/li&gt;
&lt;li&gt;Limited formatting options.&lt;/li&gt;
&lt;li&gt;Keys can’t be rebound.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;sc-improved-sc-im&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;SC-Improved (sc-im)&lt;/h3&gt;
&lt;p&gt;I really don’t think I could use sc regularly, it seems like a
considerable time investment to master basic worksheet manipulation, and
I just don’t use a spreadsheet enough to justify that.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/scim-visual-yank.gif&quot; title=&quot;Yanking and pasting a column in sc-im, works just like in vim&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/scim-visual-yank.gif&quot; alt=&quot;sc-im Visual Yank Demo&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/scim-plot-graph.gif&quot; title=&quot;Selecting some values with visual mode, and plotting with gnuplot&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/scim-plot-graph.gif&quot; alt=&quot;sc-im Terminal Graph&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, hope is not lost. There’s a fork of sc with the interface
reworked to make it familiar to Vim users. Sc-im also adds desperately
needed features like undo/redo, rebindable keys, and import from xls and
csv files.&lt;/p&gt;
&lt;p&gt;The main attraction for me is that I was able to guess how to do some
operations without looking it up, because the bindings and commands are
all inspired by Vim. Things like visual mode, yanking and pasting,
searching, undo and redo, even &lt;code&gt;:wq!&lt;/code&gt; just work.&lt;/p&gt;
&lt;p&gt;In the demo above, you can see me using visual mode to select a
column, yanking and pasting it somewhere else, and then adjusting
widths. You can also generate a simple ASCII graphs using gnuplot. To
generate the graph in the screenshot I used &lt;code&gt;v&lt;/code&gt; to enter
visual mode, moved around to choose the data, then
&lt;code&gt;:plot bar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An especially welcome feature is the formatting options, headers can
be bold, negative values can be red, and so on. That makes reading a big
list of numbers easier.&lt;/p&gt;
&lt;section id=&quot;summary-1&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Summary&lt;/h4&gt;
&lt;table style=&quot;width:81%;&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 22%&quot;&gt;
&lt;col style=&quot;width: 58%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;As-is&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;Portable, C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Last Release&lt;/td&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/andmarti1424/sc-im&quot; class=&quot;uri&quot;&gt;https://github.com/andmarti1424/sc-im&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id=&quot;pros-1&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Pros&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Familiar bindings and commands for Vim users.&lt;/li&gt;
&lt;li&gt;Import and export data from CSV, XLS, etc.&lt;/li&gt;
&lt;li&gt;Integrated gnuplot support.&lt;/li&gt;
&lt;li&gt;Extensible, supports scripting and plugins.&lt;/li&gt;
&lt;li&gt;Still under development.&lt;/li&gt;
&lt;li&gt;Undo/Redo support.&lt;/li&gt;
&lt;li&gt;Serviceable online help.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;cons-1&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Cons&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;I ran into a few bugs while trying it out.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;gnu-oleo&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;GNU Oleo&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/oleo-simple.png&quot; title=&quot;Screenshot of GNU Oleo&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/oleo-simple.png&quot; alt=&quot;Oleo Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The original GNU spreadsheet. I had to fix some bitrot to get it to
compile on a modern system. The code was mostly well written, just some
outdated C syntax.&lt;/p&gt;
&lt;p&gt;Oleo uses r1c1 cell references by default. The only reason I know
about r1c1 is because I saw a &lt;a href=&quot;https://www.youtube.com/watch?v=0nbkaYsR94c&quot;&gt;fun talk&lt;/a&gt; by Joel
Spolsky who said anything else is “baby mode” 😂.&lt;/p&gt;
&lt;p&gt;It’s worth watching the talk, most of what he says applies to any
spreadsheet. Still, it feels pretty foreign if you’re not used to it, so
luckily you can disable it. I guess I’m a spreadsheet baby! 👶&lt;/p&gt;
&lt;p&gt;Where sc-im navigation feels familiar, Oleo was clearly designed with
Emacs users in mind. For example, disabling r1c1 mode requires
&lt;code&gt;M-x set-option a0&lt;/code&gt;. Still, the bindings are configurable
with keymaps and macros, so you can adjust it.&lt;/p&gt;
&lt;p&gt;There is builtin graphing, but by default it uses xterm’s tektronix
mode - I don’t think any other terminals support that except real xterm.
If you’re not using xterm, Oleo can save an image file that can be
viewed externally. There is a simple in-cell bar chart format
though.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/oleo-graph-cell.gif&quot; title=&quot;Copying Values, and changing format in Oleo&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/oleo-graph-cell.gif&quot; alt=&quot;Oleo Graph Format&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Worksheets can update automatically, so you can make a live dashboard
that ingests stock prices or other data. I think this is surprisingly
unique, the other spreadsheets I’ve tested only do recalculation when
you make a change. Oleo has an option called &lt;code&gt;ticks&lt;/code&gt; that
lets you specify an automatic update frequency.&lt;/p&gt;
&lt;p&gt;Here’s a silly demo that updates with random numbers, but I can
imagine how I might make a spreadsheet that fetches live data for a
dashboard.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/oleo-tick-update.gif&quot; title=&quot;Live updating dashboard&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/oleo-tick-update.gif&quot; alt=&quot;Oleo Graph Format&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Oleo has some major missing features, as far as I can tell there is
no ability to sort a range, and there are very limited formatting
options. That said, the features that are implemented are quite
polished.&lt;/p&gt;
&lt;section id=&quot;summary-2&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;table style=&quot;width:76%;&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 22%&quot;&gt;
&lt;col style=&quot;width: 54%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;GPL-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;Portable, C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Last Release&lt;/td&gt;
&lt;td&gt;2001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.gnu.org/software/oleo/&quot; class=&quot;uri&quot;&gt;https://www.gnu.org/software/oleo/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id=&quot;pros-2&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional live recalculation.&lt;/li&gt;
&lt;li&gt;Optional mouse support.&lt;/li&gt;
&lt;li&gt;Extensive keymap rebinding.&lt;/li&gt;
&lt;li&gt;Built in graphing, including simple in-cell graphs.&lt;/li&gt;
&lt;li&gt;Native import support.&lt;/li&gt;
&lt;li&gt;Macros and scripting.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;cons-2&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Default keybindings only an Emacs user could love.&lt;/li&gt;
&lt;li&gt;Steep learning curve.&lt;/li&gt;
&lt;li&gt;Some missing standard spreadsheet features.&lt;/li&gt;
&lt;li&gt;Limited formatting options.&lt;/li&gt;
&lt;li&gt;Terrible online help, and cryptic error messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;lotus-1-2-3&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Lotus 1-2-3&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-simple.png&quot; title=&quot;Simple Lotus 1-2-3 spreadsheet&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-simple.png&quot; alt=&quot;Lotus 1-2-3 Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The original IBM PC killer application, with millions of active users
throughout the 80s and 90s.&lt;/p&gt;
&lt;p&gt;I was able to find a boxed copy on eBay with the original shrinkwrap
for around $20, and installed it on Linux under DOSEMU2. Using ancient
software with emulation won’t appeal to everyone, but I quite enjoy it.
DOSEMU2 let’s old DOS software access the filesystem, run host commands
as if they’re DOS commands, and makes them look indistinguishable from
native ncurses applications.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-box-back.jpg&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-box-back.jpg&quot; alt=&quot;Lotus 1-2-3 Box Back&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-box-front.jpg&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-box-front.jpg&quot; alt=&quot;Lotus 1-2-3 Box Front&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt; Note:
1-2-3 is available from reputable historical software archives like the
&lt;a href=&quot;https://archive.org/details/software&quot;&gt;Internet Archive&lt;/a&gt; and
&lt;a href=&quot;https://winworldpc.com/library/&quot;&gt;WinWorld&lt;/a&gt;. Original copies
are available on eBay for around $5-$20 depending on condition. I paid a
little more to get a boxed copy with a printed manual.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While the UNIX spreadsheets all have a bare bones interface, the
MS-DOS alternatives have very polished usability. 1-2-3 has
context-sensitive help with detailed examples, menus, and dialogs to
setup complex functions. If a formula or command generates an error,
pressing &lt;code&gt;F1&lt;/code&gt; will usually give you a detailed description of
the error, suggest causes and solutions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-online-help-func.png&quot; title=&quot;The @function help has detailed examples and documentation&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-online-help-func.png&quot; alt=&quot;Lotus 1-2-3 Help&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you’ve used any spreadsheet before, you don’t really need to read
any documentation to get started. Perhaps the only thing you need to
know is that the 1-2-3 menu is activated with the &lt;code&gt;/&lt;/code&gt; key.
Interestingly, that menu was the subject of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Lotus_Dev._Corp._v._Borland_Int%27l,_Inc.&quot;&gt;major
lawsuit&lt;/a&gt; in the 90s.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-sort-range.gif&quot; title=&quot;Most 1-2-3 features are accessed through menus&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-sort-range.gif&quot; alt=&quot;Lotus 1-2-3 Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don’t mind learning the common commands, but if I only want
something occasionally I know I just won’t bother using it if I have to
lookup the syntax every time. Here’s a good example, sc and Oleo both
support column freezing (that’s where you make a column always visible
as your browse around, so you can always see the headings). I
&lt;em&gt;could&lt;/em&gt; live without it, but it’s so easy to find in 1-2-3 that
it’s no effort at all.&lt;/p&gt;
&lt;p&gt;1-2-3 can import from CSV and a few other formats but with serious
limitations: No line can exceed 512 characters, which seems ridiculously
small to me. Worksheets can contain thousands of columns though, so I
suppose you just need to import a larger CSV dataset a few columns at a
time.&lt;/p&gt;
&lt;p&gt;There are extensive formatting options, but very few of them are
actually displayed in text mode. For example, you can make a label bold,
but unless you print it or switch to graphical mode, it doesn’t look
bold. Some options work though, like setting negative values red.&lt;/p&gt;
&lt;section id=&quot;graphs-in-1-2-3&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Graphs in 1-2-3&lt;/h3&gt;
&lt;p&gt;You can create a graph and give it a name (e.g.&amp;nbsp;EXPENSES), then
assign cell ranges to axes interactively. There are dialogs and menus to
help choose display options, things like graph type (e.g.&amp;nbsp;bar/pie/line),
colors, and so on. The process is easy, mostly just following
prompts.&lt;/p&gt;
&lt;p&gt;You can insert the graph into the sheet, but only a placeholder is
inserted - if you want to view it you need to send it to the printer or
save to a file. This actually works pretty well in DOSEMU2, I can hit a
key and the graph opens in my browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-print-graph.gif&quot; title=&quot;Generating a simple graph in Lotus 1-2-3.&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-print-graph.gif&quot; alt=&quot;Lotus 1-2-3 Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here’s an &lt;a href=&quot;https://lock.cmpxchg8b.com/files/123-sample-graph.pdf&quot;&gt;example graph&lt;/a&gt;, if
you want to see it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;using-macros&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Using Macros&lt;/h3&gt;
&lt;p&gt;To see how macros worked, I tried automatically updating a
spreadsheet with stock prices so I wouldn’t have to enter them manually.
This wasn’t too difficult with DOSEMU, first I created a quick shell
script to print stock prices to &lt;code&gt;stdout&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode bash&quot;&gt;&lt;code class=&quot;sourceCode bash&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;co&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;bu&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;https://query1.finance.yahoo.com/v7/finance/quote&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;bu&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;.quoteResponse.result[].regularMarketPrice&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/spreadsheet.html#cb2-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;ex&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;at&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;${api}&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;?symbols=&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;${1&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;:-&lt;/span&gt;^GSPC&lt;span class=&quot;va&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ex&quot;&gt;jq&lt;/span&gt; &lt;span class=&quot;at&quot;&gt;-ar&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;${filter}&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can call it from within DOSEMU using the &lt;code&gt;unix&lt;/code&gt;
command, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;unix stocks.sh GOOGL
1764.13&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So an example 1-2-3 macro would look like this, where &lt;code&gt;C1&lt;/code&gt;
is the cell I want the result stored.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{SYSTEM &quot;UNIX stocks.sh GOOGL &amp;gt; %TEMP%\STOCKS.TXT&quot;}
{OPEN &quot;C:\TMP\STOCKS.TXT&quot;, &quot;r&quot;}
{READLN C1}
{CLOSE}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-macro.gif&quot; title=&quot;Using macros to update stock prices.&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-macro.gif&quot; alt=&quot;Lotus 1-2-3 Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the screenshot you can see I’ve called this macro
&lt;code&gt;{GETSTOCKS}&lt;/code&gt;, and I’ve configured it to run automatically
when I open the file. Note that you can hide the macros if you don’t
want them to clutter up your spreadsheet.&lt;/p&gt;
&lt;p&gt;I think this worked pretty well, who would have thought 30 year old
MS-DOS abandonware could use a JSON API 😂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;summary-3&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;table style=&quot;width:99%;&quot;&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 15%&quot;&gt;
&lt;col style=&quot;width: 61%&quot;&gt;
&lt;col style=&quot;width: 22%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Abandonware, Originally $495&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;MS-DOS&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Last Release&lt;/td&gt;
&lt;td&gt;1994 (for MS-DOS)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td colspan=&quot;2&quot;&gt;&lt;a href=&quot;https://web.archive.org/web/19961221020320/http://www.lotus.com/corpcomm/2612.htm&quot; class=&quot;uri&quot;&gt;https://web.archive.org/web/19961221020320/http://www.lotus.com/corpcomm/2612.htm&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id=&quot;pros-3&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Excellent documentation and online help.&lt;/li&gt;
&lt;li&gt;Menu system makes finding features easy.&lt;/li&gt;
&lt;li&gt;Minimal need to memorize all but the most common commands.&lt;/li&gt;
&lt;li&gt;Highly configurable graphs.&lt;/li&gt;
&lt;li&gt;Imports from CSV, but with some size limitations.&lt;/li&gt;
&lt;li&gt;Excellent macro support, with record, replay, and debugging.&lt;/li&gt;
&lt;li&gt;Undo support.&lt;/li&gt;
&lt;li&gt;Optional mouse support.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id=&quot;cons-3&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Cons&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Only supports VGA text resolutions.&lt;/del&gt; I &lt;a href=&quot;https://lock.cmpxchg8b.com/lotus123.html&quot;&gt;fixed&lt;/a&gt; this!&lt;/li&gt;
&lt;li&gt;Most cell formatting options are not displayed.&lt;/li&gt;
&lt;li&gt;Proprietary.&lt;/li&gt;
&lt;li&gt;Non-native.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;borland-quattro-pro&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Borland Quattro Pro&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/quattro-simple.png&quot; title=&quot;Quattro Pro Screenshot&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/quattro-simple.png&quot; alt=&quot;Quattro Pro Screenshot&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Perhaps 1-2-3’s biggest competitor, Quattro Pro was later acquired by
Corel who still develop it today. The last console version is from 1993,
and I was able to find a new-old-stock copy on eBay for around $30.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/quattro-box-back.jpg&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/quattro-box-back.jpg&quot; alt=&quot;Quattro Pro Box Back&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://lock.cmpxchg8b.com/img/quattro-box-front.jpg&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/quattro-box-front.jpg&quot; alt=&quot;Quattro Pro Box Front&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt; Note: Quattro Pro is available from
reputable historical software archives like the &lt;a href=&quot;https://archive.org/details/software&quot;&gt;Internet Archive&lt;/a&gt; and &lt;a href=&quot;https://winworldpc.com/library/&quot;&gt;WinWorld&lt;/a&gt;. Original copies are
available on eBay for around $5-$20 depending on condition. I paid a
little more to get a boxed copy with a printed manual. &lt;br&gt; &lt;br&gt;
Apparently I got some kind of limited edition copy given to the
development team…? &lt;code&gt;¯\_(ツ)_/¯&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/quattro-menus.gif&quot; title=&quot;Browsing Quattro Pro Menus&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/quattro-menus.gif&quot; alt=&quot;Quattro Pro Menus&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Like Lotus 1-2-3, I use it in Linux using DOSEMU2. I think my first
impression of Quattro is how modern it looks, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Turbo_Vision&quot;&gt;Turbo Vision&lt;/a&gt;
interface has aged remarkably well. Menus are organized how you would
expect them today, things like File/Open, Edit/Copy, etc.&lt;/p&gt;
&lt;p&gt;Quattro Pro is the only spreadsheet I’ve tested that supports box
drawing characters, so you can draw cell borders. More formatting
options are always welcome, and the graphs in Quattro also look pretty
good. Here is the &lt;a href=&quot;https://lock.cmpxchg8b.com/files/qpro-sample-graph.pdf&quot;&gt;same example&lt;/a&gt;
from Lotus 1-2-3.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/quattro-graph.gif&quot; title=&quot;Generating a Graph in Quattro Pro&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/quattro-graph.gif&quot; alt=&quot;Quattro Pro Graphs&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All the standard features work well, and are easy to discover through
the menus. The interface is very configurable, you can rearrange and
edit menus and keybindings. If you wanted to use it with a mouse you can
even make a toolbar.&lt;/p&gt;
&lt;p&gt;In general, Quattro Pro has feature parity with 1-2-3, but just feels
more polished. Perhaps one exception, you can import data from CSV, but
astonishingly there is a maximum line length limit of just 254
characters. That just seems ridiculous.&lt;/p&gt;
&lt;p&gt;I browsed around old usenet archives and found that Borland released
an official workaround, a macro called DELREAD in their &lt;a href=&quot;http://cd.textfiles.com/ems/emspro17/disk3/SSUTIL/QPROUTIL.ZIP&quot;&gt;Quattro
Pro Utilities&lt;/a&gt; collection. I guess even in 1993 people thought that
limit was crazy.&lt;/p&gt;
&lt;section id=&quot;summary-4&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 18%&quot;&gt;
&lt;col style=&quot;width: 81%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Abandonware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;MS-DOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Last Release&lt;/td&gt;
&lt;td&gt;1993 (for MS-DOS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Website&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://web.archive.org/web/19961221133010/http://www.borland.com/&quot; class=&quot;uri&quot;&gt;https://web.archive.org/web/19961221133010/http://www.borland.com/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id=&quot;pros-4&quot; class=&quot;level4&quot;&gt;
&lt;h4&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Possibly the easiest to use, familiar menu layout.&lt;/li&gt;
&lt;li&gt;Highly configurable. Menus, colors, toolbars and keys can all be
redefined.&lt;/li&gt;
&lt;li&gt;Optional mouse support.&lt;/li&gt;
&lt;li&gt;More formatting options that display in the console.&lt;/li&gt;
&lt;li&gt;Excellent online help, including context sensitive help.&lt;/li&gt;
&lt;li&gt;Excellent macro support, even better debugger than 1-2-3.&lt;/li&gt;
&lt;li&gt;Nicer looking graphs than 1-2-3.&lt;/li&gt;
&lt;li&gt;Very polished usability, dialogs and menus are easy to
navigate.&lt;/li&gt;
&lt;li&gt;Import and export to various other formats.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id=&quot;cons-4&quot; class=&quot;level5&quot;&gt;
&lt;h5&gt;Cons&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Only supports VGA text resolutions.&lt;/li&gt;
&lt;li&gt;Proprietary.&lt;/li&gt;
&lt;li&gt;Non-native.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I’m a big enough nerd that I rather enjoy using retro software, and I
think I’m going to start using Lotus 1-2-3. There’s no question the
MS-DOS packages are the more polished of the console spreadsheets I’ve
tested.&lt;/p&gt;
&lt;p&gt;I really do like sc-im, and I’m glad it exists. I think as a vim user
I could be quite efficient at basic manipulation. That said, I think
there are features I like but just wouldn’t use because I’d have to look
up how to use them every time. Quattro Pro is accessible enough that I
can poke around in menus when I need to use a feature I’m not familiar
with.&lt;/p&gt;
&lt;p&gt;Perhaps I’ll update this article in 3 months with my experiences and
describe any limitations I ran into… or if I gave up and switched to
something else. 😅&lt;/p&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/spreadsheet.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/spreadsheet.html</guid><author>Tavis Ormandy</author></item><item><title>Read about me trying out some old MS-DOS software, Lotus Agenda.</title><description>&lt;header id=&quot;title-block-header&quot;&gt;
&lt;h1 class=&quot;title&quot;&gt;Lotus Agenda.&lt;/h1&gt;
&lt;p class=&quot;author&quot;&gt;Tavis Ormandy&lt;/p&gt;
&lt;p class=&quot;date&quot;&gt;$Id: a07cf90837a3c4373b82d6724b97593810766af7 $&lt;/p&gt;
&lt;/header&gt;

&lt;section id=&quot;intro&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;I never used &lt;a href=&quot;https://en.wikipedia.org/wiki/Lotus_Agenda&quot;&gt;Lotus Agenda&lt;/a&gt;, but
I’m told it was a popular productivity tool for MS-DOS in the late 80s.
I’ve been on a retro software rediscovery kick lately, so I’ve decided
to give it a whirl and write about my experiences. There is something
that appeals to me about using long-abandoned software. Perhaps it’s
update fatigue, there’s certainly no need to dread a major update
breaking something!&lt;/p&gt;
&lt;p&gt;Regardless, I’ve always enjoyed finding new productivity tools to try
out, and I’m not afraid of steep learning curves or getting my hands
dirty. I’ll usually choose powerful and flexible software over
simplicity.&lt;/p&gt;
&lt;p&gt;At the moment I mostly use &lt;a href=&quot;https://taskwarrior.org/&quot;&gt;taskwarrior&lt;/a&gt;, but I’ve lost count of
all the others I’ve tried!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;about-agenda&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;About Agenda&lt;/h1&gt;
&lt;p&gt;Agenda is a PIM, a &lt;em&gt;Personal Information Manager&lt;/em&gt;. That term
has fallen out of fashion, I think a quick summary might be “anything
that manages those small pieces of information we all deal with”. Things
like contacts, todo lists, notes, and so on.&lt;/p&gt;
&lt;p&gt;I found a 1989 episode of the TV show &lt;a href=&quot;https://en.wikipedia.org/wiki/Computer_Chronicles&quot;&gt;Computer
Chronicles&lt;/a&gt; that discussed how people thought about PIMs at the
time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/HB38RA5xYY0?t=1281&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/hqdefault.jpg&quot; alt=&quot;Computer Chronicles&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At the &lt;a href=&quot;https://youtu.be/HB38RA5xYY0?t=1281&quot;&gt;21-minute
mark&lt;/a&gt; there’s a demonstration of Lotus Agenda, but it’s not easy to
follow, watch the clip and you’ll see what I mean. Still, you do see
some interesting features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some form of NLP is used to extract dates and names from free-form
text.&lt;/li&gt;
&lt;li&gt;Categories are a little like what we would call tags today.&lt;/li&gt;
&lt;li&gt;You can create custom dashboards called views.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apparently this was an $800 software package (That’s $395 adjusted
for inflation from 1989), yikes! You don’t have to pay that, Lotus made
it &lt;a href=&quot;http://www2.support.lotus.com/ftp/pub/desktop/Agenda/dos/2.0/misc/&quot;&gt;available&lt;/a&gt;
for free when development ceased.&lt;/p&gt;
&lt;p&gt;In preparation for trying out Agenda, I found a copy of the original
manuals on eBay for a few dollars. Just look at this monster, the user
guide alone is over 700 pages, that’s not including the supplementary
guides. The supplements I have are &lt;em&gt;Working with Macros&lt;/em&gt;,
&lt;em&gt;Working with Definition Files&lt;/em&gt;, &lt;em&gt;Setting up Agenda&lt;/em&gt;, and
a few miscellaneous leaflets.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-manual.jpg&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-manual.jpg&quot; alt=&quot;Lotus Agenda Manual&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I guess that’s my bedtime reading taken care of for a while. I
actually received the macro reference still in the original shrink wrap,
it almost seems a shame to open it!&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;getting-started&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Getting Started&lt;/h1&gt;
&lt;p&gt;I’m using &lt;a href=&quot;https://github.com/dosemu2/dosemu2&quot;&gt;DOSEMU2&lt;/a&gt; to
run Agenda, which also works with WSL on Windows. The primary benefit of
DOSEMU over other options is that it has a terminal mode that translates
VGA text mode to terminal escape sequences. This makes old MS-DOS
applications look and feel like native ncurses applications.&lt;/p&gt;
&lt;p&gt;Terminal tools like GNU screen work, you can copy and paste between
native applications, and you can use it over ssh. Naturally, it also
supports filesystem and shell redirection, so you can access host
directories, and execute host commands as if they were DOS commands.&lt;/p&gt;
&lt;p&gt;Here’s how Agenda looks when it’s first installed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-startup.gif&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-startup.gif&quot; alt=&quot;Agenda Startup&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You’ve probably noticed the empty screen regions, well, that’s
because Agenda only supports standard &lt;a href=&quot;https://en.wikipedia.org/wiki/VGA_text_mode#PC_common_text_modes&quot;&gt;VGA
Text Modes&lt;/a&gt;. I usually use a 100x30 terminal, so that leaves some
blank space. If you use &lt;a href=&quot;https://www.gnu.org/software/screen/&quot;&gt;GNU screen&lt;/a&gt; you can just
put &lt;a href=&quot;https://lock.cmpxchg8b.com/img/agenda-screen.png&quot;&gt;something else&lt;/a&gt; in there, how
about &lt;code&gt;ncal&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;I configured DOSEMU to map a few directories so I can save my files
in the usual places, you just need to add something like
&lt;code&gt;lredir I: \\linux\fs\home\taviso\Documents&lt;/code&gt; to
&lt;code&gt;autoexec.bat&lt;/code&gt;.&lt;/p&gt;
&lt;section id=&quot;learning-agenda&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Learning Agenda&lt;/h2&gt;
&lt;p&gt;I started making my way through the Quick Start guide, I found a &lt;a href=&quot;https://archive.org/details/lotus-agenda-users-guide&quot;&gt;scan
online&lt;/a&gt; if you want to take a look. The guide has you use Agenda as a
task planner to enter upcoming events, like &lt;em&gt;“Call Sarah this Friday
to give her feedback on her proposal”&lt;/em&gt;, and demonstrates how Agenda
can extract dates and activities from text you enter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-quickstart.gif&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-quickstart.gif&quot; alt=&quot;Agenda Quick Start&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is a gif to demonstrate, I enter a task and it correctly
populates some of the other columns. Playing around with different ways
of writing tasks, I find the metadata extraction works quite well. Time
specification parsing in particular is quite polished, things like
“&lt;em&gt;Check data retention policy every four months starting
Tuesday&lt;/em&gt;” just work. I’m also relieved that it appears to be Y2K
compliant (haha!).&lt;/p&gt;
&lt;p&gt;The pitch in the Quick Start Guide is that if you have a call with
Tom, you can quickly generate a report for all the things you wanted to
discuss. If you’re going to a specific location, you can list all the
things you needed to do there, and so on.&lt;/p&gt;
&lt;p&gt;Agenda is flexible software that can be used for more than just task
management, but to keep this article brief I’m sticking to using it as a
planner.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;using-the-planner&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Using The Planner&lt;/h2&gt;
&lt;p&gt;I’ve entered enough tasks now to get a feel for usage, and all the
standard stuff works well. Things like notes, setting alarms, adding
contacts, assigning priorities, searching and sorting, and so on. The
sort of things that get tedious can be automated, like marking all the
results of a search, then bulk assigning or marking done.&lt;/p&gt;
&lt;p&gt;Agenda is pretty stable, I guess some of those &lt;code&gt;$395&lt;/code&gt;
dollars went towards QA. I did find one crash, I was trying to make an
assignment action for a pseudo category in the show view! I think I’m
the first person to discover this, I can’t find any other references to
it!&lt;/p&gt;
&lt;p&gt;I had to change my workflow a bit, I’m used to organizing my tasks
into subtasks, so for example I might have a to-do list like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Deploy Death Star
&lt;ul&gt;
&lt;li&gt;Choose Target&lt;/li&gt;
&lt;li&gt;Verify Design
&lt;ul&gt;
&lt;li&gt;Check for weak points&lt;/li&gt;
&lt;li&gt;Call maintenance&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Assign Staff&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Call Mom&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;That isn’t really how Agenda works, instead you would create each of
these tasks and assign them to the same project. This is a shift for me,
but it has benefits, such as allowing a task to be assigned to multiple
projects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-projects.gif&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-projects.gif&quot; alt=&quot;Agenda Projects&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can see in this clip I’ve changed the layout, you’re free to add
or remove columns and change their order, width, and so on. You can also
add filters, so you might want to only see Tasks due today, for
example.&lt;/p&gt;
&lt;p&gt;You can save an unlimited number layouts (Agenda calls these Views).
You can switch to the view that suits your current task, preferred level
of information density, and so on.&lt;/p&gt;
&lt;p&gt;You might have a “Chores” view while at home that just shows the
things you need to do at home, or a “Development” view while working on
code. This can let you display your tasks in the most appropriate
way.&lt;/p&gt;
&lt;p&gt;Honestly, I think Agenda does this quite well, and getting a finely
tuned view with sections and filters set up precisely was quite
satisfying. There are different kinds of views as well, such as datebook
view (like a calendar), expense view (for tallying expenses associated
with a project), and so on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/agenda-view.png&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/agenda-view.png&quot; alt=&quot;Agenda View&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my main dashboard I have three sections:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 45%&quot;&gt;
&lt;col style=&quot;width: 54%&quot;&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Things I have due within a week&lt;/td&gt;
&lt;td&gt;Tasks &lt;code&gt;[-Done,When(&amp;lt;-&amp;gt;A week from today)]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;Things that dont have a due date&lt;/td&gt;
&lt;td&gt;Tasks &lt;code&gt;[-Done,-When]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;Things I’ve completed recently&lt;/td&gt;
&lt;td&gt;Done &lt;code&gt;[Done(2 days ago&amp;lt;-&amp;gt;)]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I actually wrote the filter as “day before yesterday”, and it
automatically rewrote it to 2 days ago. Neat. I have a few more views
with more columns and project filters that I switch to as I need them.
You can either press F8 to browse the list of views, or use Alt-N (Next)
and Alt-P (Prev) to cycle between them.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;entering-notes&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Entering Notes&lt;/h2&gt;
&lt;p&gt;Agenda does have a built-in editor for keeping notes. It’s okay. It
supports a few basic editing options and has rebindable keys and macros.
Luckily it supports importing external files, so you can just use
Vim.&lt;/p&gt;
&lt;p&gt;In fact, I wrote a macro to switch to vim whenever I type Ctrl-F5 in
the editor. Here is a clip of me opening the Macro Manager, showing that
I have an “Editor” macro bound to {CtlF5}, and then editing a note in
Vim.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-editor.gif&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/lotus-agenda-editor.gif&quot; alt=&quot;Agenda Editor&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wish I could say the macro language is pretty, but it is not. I
think it was inspired by the macro language from 1-2-3, it’s geared
towards reading whats on the screen and simulating keystrokes. It has
basic control flow, string manipulation, and so on. It’s functional.&lt;/p&gt;
&lt;p&gt;Here is that macro you just saw in the editor clip.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{Editor}
{LET;%FILE;C:\TMP\NOTE.TXT}
{WINDOWSOFF}
{F10}E{TYPE;%FILE}
{ENTER}
{ENTER}
{SELECTION;File already exists}
{IF;(#PROMPT &amp;lt;&amp;gt; File already exists:);EDITOR}
{F3}Replace{ENTER}
{LABEL;EDITOR}
{F5}
{F10}ULVIM {TYPE;%FILE}
{ENTER}
{F5}
{F10}C
{ENTER}
{F10}I{TYPE;%FILE}
{ENTER}
{ENTER}
{WINDOWSON}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can say a few positive things about the macro language, the manual
is pretty good and it comes with lots of samples. It can automate all
parts of Agenda, but I’ve mostly just used it to rebind keys.&lt;/p&gt;
&lt;p&gt;In fact, in the 1980s third party developers wrote commercial add-ons
for Agenda. Apparently there was a popular package called &lt;a href=&quot;http://www.bobnewell.net/filez/ppdoc.pdf&quot;&gt;President’s Planner&lt;/a&gt;.
I haven’t tried it, but I suppose I have a morbid curiousity what an
application written in this language can do!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;importing-and-exporting&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Importing and Exporting&lt;/h1&gt;
&lt;p&gt;You probably need to use other applications or services, and sync
your data with your phone. Writing and reading files from outside DOSEMU
is no problem, so if you just want to sync files this is no problem.&lt;/p&gt;
&lt;p&gt;As it’s a terminal application you can also just SSH in and run
it.&lt;/p&gt;
&lt;p&gt;You probably also want to have your appointments sync with your
calendar or something.&lt;/p&gt;
&lt;section id=&quot;export&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Export&lt;/h2&gt;
&lt;p&gt;There are two ways to export data from Agenda. If you have a
commandline tool that you can pass arguments to, then you can write a
macro that will invoke it.&lt;/p&gt;
&lt;p&gt;Otherwise, you can export your data to a file.&lt;/p&gt;
&lt;section id=&quot;files&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Files&lt;/h3&gt;
&lt;p&gt;Agenda can export items to a format called STF, &lt;em&gt;Structured Text
File&lt;/em&gt;. The specification for that format is (mostly) documented in
the manual, but it didn’t catch on.&lt;/p&gt;
&lt;p&gt;I wrote a quick parser that can convert it to JSON, so now you can
use modern tools like &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; to
manipulate and transform the data however you wish.&lt;/p&gt;
&lt;p&gt;You can download it &lt;a href=&quot;https://github.com/taviso/stfjson&quot;&gt;here&lt;/a&gt;, here are some
examples.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Print a list of all items.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ ./stfjson &amp;lt; transfer.stf | jq &#39;.[].items[].text&#39;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Show all items with a due date.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ ./stfjson &amp;lt; transfer.stf | jq &#39;.[].items[] | select(.categories[].name==&quot;\\When&quot;)&#39;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so on, there are more examples in the README. If you can exchange
data with other apps, you can now use &lt;code&gt;stfjson&lt;/code&gt; to generate
the correct format.&lt;/p&gt;
&lt;p&gt;You can automate exports, Agenda has “Special Actions” in the
category options. Alternatively, if it’s just a one off or for a macro,
you can use the &lt;code&gt;Transfer &amp;gt; Export&lt;/code&gt; command.&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;commands&quot; class=&quot;level3&quot;&gt;
&lt;h3&gt;Commands&lt;/h3&gt;
&lt;p&gt;In DOSEMU, the &lt;code&gt;UNIX&lt;/code&gt; command will invoke a shell command
on the host.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\&amp;gt;unix uname
Linux&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there is a commandline tool that will import data, e.g.&amp;nbsp;a
TaskWarrior user might use
&lt;code&gt;task add drop off laundry at dry cleaners&lt;/code&gt;, then you can
create a macro in Agenda that simply launches that command.&lt;/p&gt;
&lt;p&gt;You can use something like
&lt;code&gt;{F10}ULUNIX task {TYPE;%TASKTEXT}&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;import&quot; class=&quot;level2&quot;&gt;
&lt;h2&gt;Import&lt;/h2&gt;
&lt;p&gt;Surprisingly, Agenda supports importing arbitrary text data. One of
the manuals that came with agenda was &lt;em&gt;Working with Definition
Files&lt;/em&gt;, which explains how to write a configuration file that allow
Agenda to parse anything.&lt;/p&gt;
&lt;p&gt;It even has a Regular Expression tutorial, pretty impressive for a
1980s consumer product.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id=&quot;conclusion&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I quite like Agenda. It does many things well, but it’s absolutely
true you could replicate most of it’s functionality with modern tools.
However, I do enjoy using it, and I’m a big enough nerd that I quite
like the challenge of using retro software.&lt;/p&gt;
&lt;p&gt;I think the closest modern equivalent to Agenda would be &lt;a href=&quot;https://github.com/tools-life/taskwiki&quot;&gt;taskwiki&lt;/a&gt;. It’s not a
perfect match, but if you liked some of what you saw here but are not
interested in retro software, try it out!&lt;/p&gt;
&lt;p&gt;I’m still using Agenda after two weeks, and about 40% of the way
through the manual 😂&lt;/p&gt;
&lt;/section&gt;
&lt;section id=&quot;resouces&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Resouces&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://agenda.bobnewell.net/&quot;&gt;Bob Newell’s Agenda Page&lt;/a&gt;
has lots of old documentation. In particular, I found this old &lt;a href=&quot;http://www.bobnewell.net/agenda/ag2qanda.txt.html&quot;&gt;Lotus Support
FAQ&lt;/a&gt; from 1990 fascinating.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id=&quot;troubleshooting&quot; class=&quot;level1&quot;&gt;
&lt;h1&gt;Troubleshooting&lt;/h1&gt;
&lt;p&gt;Anyone trying to earnestly use 30 year old software is bound to
encounter a few hiccups. Here are some of the problems I ran into. I
also have an &lt;a href=&quot;https://lock.cmpxchg8b.com/agendafaq.html&quot;&gt;FAQ&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. The installer complains about registering
disks.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; The free disk images from Lotus include some
form of copy protection. You can’t just extract the files - you need to
emulate a floppy drive. If you don’t want to bother with that, there’s a
preinstalled ZIP available &lt;a href=&quot;http://www.bobnewell.net/nucleus/bnewell.php?itemid=186&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. Some of the default keystrokes are difficult to enter
today, like Alt-F4.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; You can rebind keys using a Macro.
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Open the Macro Manager, &lt;code&gt;Alt-F3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ins&lt;/code&gt; to create a new Macro, call it “Macro”&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;F2&lt;/code&gt; to open the editor, and enter this:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;{Macro}
{AltF4}&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot; type=&quot;1&quot;&gt;
&lt;li&gt;Press &lt;code&gt;F5&lt;/code&gt; to save it, then &lt;code&gt;F6&lt;/code&gt; to open the
macro properties.&lt;/li&gt;
&lt;li&gt;Select “Attach to key”, Press &lt;code&gt;F2&lt;/code&gt; and enter
&lt;code&gt;{CtlF4}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now you can use Ctrl-F4 instead of Alt-F4. You can do the same thing
to other bindings that you don’t like.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I made Ctrl-S Save, and Ctrl-F Find. The defaults are Alt-W (??) And
Alt-F6 (???!).&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. I can’t enter Alt-F3 in my terminal, so I can’t rebind
keys!&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Download &lt;code&gt;SCANCODE.COM&lt;/code&gt; from &lt;a href=&quot;http://bretjohnson.us/&quot;&gt;here&lt;/a&gt;, it’s a DOS TSR that can simulate
keystrokes. Once you have it installed:
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Press &lt;code&gt;F10&lt;/code&gt;, Select &lt;em&gt;Utilities&lt;/em&gt;, then
&lt;em&gt;Launch&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;SCANCODE Delay 3,Alt-F3&lt;/code&gt; (If &lt;code&gt;SCANCODE&lt;/code&gt;
is not in your &lt;code&gt;%PATH%&lt;/code&gt;, type the full path,
e.g.&amp;nbsp;&lt;code&gt;C:\SCANCODE\SCANCODE.COM&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;After a few seconds, the Macro Manager will appear. I would suggest
rebinding it to Ctrl-F3, so you don’t have to do that everytime!&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. When I exit agenda, try to print something, or run
external commands I get an error like
&lt;code&gt;EInternal Error&lt;/code&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Add the line &lt;code&gt;$_umb_a0 = (off)&lt;/code&gt; to
your &lt;code&gt;.dosemurc&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. When I run Agenda, it looks garbled like this.&lt;/strong&gt;
&lt;a href=&quot;https://lock.cmpxchg8b.com/img/agenda-broken-display.png&quot;&gt;&lt;img src=&quot;https://lock.cmpxchg8b.com/img/agenda-broken-display.png&quot; alt=&quot;Broken Display&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; You are using DOSEMU with a non-standard text
mode. Here are some possible solutions, pick whichever one you prefer.
&lt;ul&gt;
&lt;li&gt;Before starting DOSEMU, type &lt;code&gt;stty cols 80&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;If you use an unusual terminal (like Windows Terminal), you might
get an error like &lt;code&gt;Operation not supported&lt;/code&gt;. A workaround is
to install &lt;code&gt;xtermcontrol&lt;/code&gt; and type
&lt;code&gt;xtermcontrol     --geometry=80x${LINES}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Use GNU screen, and create a layout for DOS applications (this is
what I do, see the next question).&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Q. Can I see your GNU screen configuration to get me
started?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; Sure, here’s my &lt;a href=&quot;https://lock.cmpxchg8b.com/files/screenrc&quot;&gt;main
screenrc&lt;/a&gt;, and here’s my &lt;a href=&quot;https://lock.cmpxchg8b.com/files/screenrc-dosemu&quot;&gt;dosemu
specific one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I use
&lt;code&gt;alias agenda=&quot;screen -c ${HOME}/.screenrc.dosemu dosemu -term -E agenda&quot;&lt;/code&gt;
in my .bashrc to start agenda.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. Can I see your DOSEMU configuration to get me
started?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Sure, &lt;a href=&quot;https://lock.cmpxchg8b.com/files/dosemurc&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. Agenda uses 100% CPU time while idle, which is stopping
my CPU from entering low power states. How do I fix that?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Update DOSEMU, I filed an &lt;a href=&quot;https://github.com/dosemu2/dosemu2/issues/1313&quot;&gt;issue&lt;/a&gt; and a
fix was committed.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Q. Agenda doesn’t use 100% CPU time, but it still uses
more than I would like.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; DOSEMU simulates timer interrupts using
&lt;code&gt;setitimer()&lt;/code&gt;, and by default it requests them as quickly as
possible. If the DOS application does a lot of work on every interrupt
(Agenda queries the time), then it can use some CPU.&lt;/p&gt;
&lt;p&gt;There’s a tradeoff here, you can simulate a slower interrupt timer
but responsiveness will go down (i.e.&amp;nbsp;how quickly the application
responds to keyboard input). You can experiment with the numbers and
find the optimal balance.&lt;/p&gt;
&lt;p&gt;If you were playing a DOS game, you would probably want it high, but
for an office application like Agenda you can turn responsiveness
waaaaay down and it’s still totally acceptable.&lt;/p&gt;
&lt;p&gt;To do that, set the &lt;code&gt;timer&lt;/code&gt; option. The default value is
18, but I find 8 to be totally acceptable, maybe even 6. You can only
change the timer setting on the commandline, not in
&lt;code&gt;.dosemurc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ dosemu -term -I &quot;timer 8&quot; -E &quot;C:\AGENDA\AGENDA.EXE&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The lower you can set this value, the lower CPU time agenda will
use.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. I get an error like “Error: A.EXE not
found”.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Agenda is not in your %PATH%, add a line like
&lt;code&gt;path %PATH%;C:\AGENDA&lt;/code&gt; to one of your initialization files,
like &lt;code&gt;fdppauto.bat&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Q. I’m using GNU screen, and some function keys produce
garbage.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; S-Lang tries to check if you’re using an
xterm compatible terminal by doing
&lt;code&gt;strncmp($TERM, &quot;xterm&quot;, 5);&lt;/code&gt;. GNU screen &lt;em&gt;is&lt;/em&gt; xterm
compatible, but it sets the terminal to
&lt;code&gt;screen.xterm-256color&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A workaround is to just make an alias for this termcap entry that
starts with “xterm”. First, get a copy of the termcap entry, e.g.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ TERM=screen.xterm-256color infocmp &amp;gt; termcap.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, add an alias at the top of the file that starts with
&lt;code&gt;xterm&lt;/code&gt;, e.g. I made mine look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;screen.xterm-256color|xterm-256color.screen|GNU Screen with xterm using 256 colors,&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now recompile it, &lt;code&gt;tic termcap.txt&lt;/code&gt;. &lt;code&gt;tic&lt;/code&gt;
automatically installs that entry in your &lt;code&gt;$HOME/.termcap&lt;/code&gt;
directory, so it should be available immediately. Now add the line
&lt;code&gt;term xterm-256color.screen&lt;/code&gt; to your .screenrc, and the
problem should be solved.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Q. I’m using GNU screen, it works but the
&lt;code&gt;Escape&lt;/code&gt; key seems slow, unreliable, or needs to be pressed
twice?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; First add the line &lt;code&gt;maptimeout 50&lt;/code&gt;
to your screenrc, and see if that helps. If it feels better but not
fixed, try tweaking the number.&lt;/p&gt;
&lt;p&gt;If it didn’t help at all, then I think your termcap definition is
wrong. Perhaps you are using a terminal that claims to be xterm
compatible, but is not.&lt;/p&gt;
&lt;p&gt;Try these steps:&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Decompile your current termcap entry,
&lt;code&gt;$ infocmp -1 &amp;gt; termcap.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Find the line that starts &lt;code&gt;kmous=&lt;/code&gt; and remove it, that’s
mouse event reporting. I’ve found this unreliable in some vte-based
terminals.&lt;/li&gt;
&lt;li&gt;Recompile it, &lt;code&gt;$ tic termcap.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See if that fixes the problem. &lt;code&gt;tic&lt;/code&gt; places compiled
definitions in your &lt;code&gt;$HOME/.termcap&lt;/code&gt; directory, you can
remove them if you want to go back.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Q. How can I print something from
Agenda?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; Add the line &lt;code&gt;$_lpt1 = &quot;dosprint&quot;&lt;/code&gt;
to your &lt;code&gt;.dosemurc&lt;/code&gt;, then create this shell script in your
$PATH called dosprint, you can adjust to your liking. The
&lt;code&gt;ps2pdfwr&lt;/code&gt; utility is part of ghostscript.&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb6&quot;&gt;&lt;pre class=&quot;sourceCode bash&quot;&gt;&lt;code class=&quot;sourceCode bash&quot;&gt;&lt;span id=&quot;cb6-1&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;co&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-2&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;bu&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;tmpfile&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;mktemp&lt;/span&gt; &lt;span class=&quot;at&quot;&gt;--tmpdir&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;${HOME}&lt;/span&gt;/Downloads &lt;span class=&quot;at&quot;&gt;--suffix&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;.pdf printjob.XXX&lt;span class=&quot;va&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-3&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-3&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ex&quot;&gt;ps2pdfwr&lt;/span&gt; &lt;span class=&quot;at&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;${tmpfile}&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class=&quot;kw&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cf&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-4&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-4&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      &lt;span class=&quot;fu&quot;&gt;xdg-open&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;va&quot;&gt;${tmpfile}&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-5&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-5&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;cf&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-6&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-6&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      &lt;span class=&quot;fu&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;at&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;${tmpfile}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-7&quot;&gt;&lt;a href=&quot;https://lock.cmpxchg8b.com/lotusagenda.html#cb6-7&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;cf&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Q. Can I see a sample of Agenda generated PostScript and/or
a sample PDF?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A.&lt;/strong&gt; Sure, here is some &lt;a href=&quot;https://lock.cmpxchg8b.com/files/agenda-sample.ps&quot;&gt;PostScript&lt;/a&gt;, and here is a &lt;a href=&quot;https://lock.cmpxchg8b.com/files/agenda-sample.pdf&quot;&gt;PDF&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;



</description><link>https://lock.cmpxchg8b.com/lotusagenda.html</link><guid isPermaLink="false">https://lock.cmpxchg8b.com/lotusagenda.html</guid><author>Tavis Ormandy</author></item></channel></rss>