diff options
20 files changed, 2751 insertions, 325 deletions
diff --git a/haunt/data/blogroll.scm b/haunt/data/blogroll.scm index a88974c..672b959 100644 --- a/haunt/data/blogroll.scm +++ b/haunt/data/blogroll.scm @@ -720,4 +720,74 @@ ("linux" "nixos" "programming" "rust")) ("Fred's notes" "https://ramblings.fred.moe/" - ("c" "programming"))) + ("c" "programming")) + ("nurpax" + "https://nurpax.github.io/" + ("6502" + "c64" + "emacs" + "functional-programming" + "haskell" + "linux" + "programming" + "retro-tech")) + ("absorptions" + "https://www.windytan.com/" + ("ham" "hardware" "sdr")) + ("see shy jo" + "https://joeyh.name/blog/" + ("functional-programming" + "ham" + "hardware" + "haskell" + "linux" + "off-grid" + "programming")) + ("TMZ Lair - Underground Coding" + "https://www.guitmz.com/" + ("linux" "programming" "security" "windows")) + ("Barracuda Blues" + "https://bendersteed.gitlab.io/index.xml" + ("common-lisp" "emacs" "guix" "linux" "lisp" "programming")) + ("../../../../overused_linux_joke/../../../../" + "https://korcankaraokcu.github.io" + ("binary-exploitation" "ctf" "reversing" "security")) + ("Hell Oh Entropy!" + "https://siddhesh.in/" + ("c" "compilers" "programming")) + ("Inside Rust" + "https://blog.rust-lang.org/inside-rust/" + ("compilers" "programming" "rust")) + ("An Index of Verisimilitudes" + "http://verisimilitudes.net/" + ("common-lisp" "lisp" "programming")) + ("byuu" + "https://byuu.net/" + ("6502" "hardware" "programming" "retro-tech")) + ("erkin's Blog" + "https://erkin.party/blog/" + ("functional-programming" "lisp" "programming" "scheme")) + ("Juraj's Blog" + "https://jborza.github.io/" + ("programming" "retro-tech")) + ("The cr.yp.to blog" + "https://blog.cr.yp.to/index.html" + ("crypto" "math")) + ("programming in the twenty-first century" + "https://prog21.dadgum.com/" + ("functional-programing" "game-development" "programming" "retro-tech")) + ("Shell is coming..." + "https://www.shelliscoming.com/" + ("binary-exploitation" "reversing" "security" "windows" "x86")) + ("(pixel)desu.blog" + "https://desu.blog/" + ("reversing" "video-games")) + ("Mildly entertainingᵝ" + "https://beepb00p.xyz/" + ("emacs" "programming")) + ("Thomas' place in cyberspace" + "https://www.härdin.se/category/blog.html" + ("hardware" "linux" "programming" "security")) + ("acloudtree" + "https://www.acloudtree.com/post/" + ("programming" "security"))) diff --git a/haunt/data/bookmarks.scm b/haunt/data/bookmarks.scm index 13df748..cb0b4b6 100644 --- a/haunt/data/bookmarks.scm +++ b/haunt/data/bookmarks.scm @@ -1,4 +1,4 @@ -'(("Finding and Exploiting Hidden Features of Animal Crossings NES Emulator" +`(("Finding and Exploiting Hidden Features of Animal Crossings NES Emulator" "https://jamchamb.github.io/2018/07/11/animal-crossing-nes-emulator-hacks.html" ("binary-exploitation" "c" @@ -9,6 +9,15 @@ "retro-tech" "reversing" "video-games")) + ("Arbitrary Code Execution in Ocarina of Time" + "https://www.youtube.com/watch?v=RoEmGCNsbno" + ("binary-exploitation" + "game-hacking" + "mips" + "n64" + "security" + "video" + "video-games")) ("Disassembling Jak & Daxter" "http://www.codersnotes.com/notes/disassembling-jak/" ("lisp" @@ -31,15 +40,36 @@ ("Zelda Screen Transitions are Undefined Behavior" "https://gridbugs.org/zelda-screen-transitions-are-undefined-behaviour/" ("6502" "nes" "retro-tech" "reversing" "video-games")) + ("Sonic Battle (GBA) Renderer Series" + "https://fouramgames.com/blog/sonic-battle-renderer" + ("graphics" "hardware" "reversing" "retro-tech")) + ("How Much of a Genius-Level Move Was Using Binary Space Partitioning in Doom?" + "https://twobithistory.org/2019/11/06/doom-bsp.html" + ("graphics" "math" "programming" "video-games")) ("Investigating a recent ebook DRM system (c. 2018)" "https://yingtongli.me/blog/2019/03/28/drm2.html" ("android" "arm" "reversing")) ("Looking inside the box" "https://anvilventures.com/blog/looking-inside-the-box.html" ("python" "reversing")) + ("Analysis of the 2015 ESET Crackme challenge" + "https://lyxica.github.io/ESET.pdf" + ("ctf" "reversing" "x86")) ("Adventures in reverse engineering Broadcom NIC firmware" "https://www.devever.net/~hl/ortega" ("arm" "hardware" "reversing" "x86")) + ("GPS, Galileo & More: How do they work & what happened during the big outage?" + "https://ds9a.nl/articles/posts/gps-gnss-how-do-they-work/" + ("ham")) + ("[DSCTF 2019] CPU Adventure – Unknown CPU Reversing" + "https://www.robertxiao.ca/hacking/dsctf-2019-cpu-adventure-unknown-cpu-reversing/" + ("ctf" "reversing" "writeup")) + ("You Won't Believe This One Weird CPU Instruction!" + "https://vaibhavsagar.com/blog/2019/09/08/popcount/" + ("hardware" "historical" "optimization" "x86")) + ("Glitching a $20k Piece of History" + "https://yifan.lu/2019/08/16/glitching-a-20k-piece-of-history/" + ("arm" "hardware" "reversing")) ("A Great Old-Timey Game-Programming Hack" "http://blog.moertel.com/posts/2013-12-14-great-old-timey-game-programming-hack.html" ("6502" @@ -64,6 +94,9 @@ ("Code Conversion Language" "http://emacsninja.com/posts/code-conversion-language.html" ("c" "emacs" "emacs-lisp" "lisp" "optimization" "programming" "reversing")) + ("The General Problem" + "https://generalproblem.net/" + ("compilers" "functional-programming" "guile" "lisp" "programming" "scheme" "x86")) ("What the Hero Sees: Field-of-View for Roguelikes " "http://journal.stuffwithstuff.com/2015/09/07/what-the-hero-sees/" ("programming" @@ -96,6 +129,9 @@ ("A Road to Common Lisp" "http://stevelosh.com/blog/2018/08/a-road-to-common-lisp/" ("common-lisp" "lisp" "programming")) + ("MOST-POSITIVE-BIGNUM" + "https://www.jwz.org/blog/2008/03/most-positive-bignum/" + ("historical" "lisp")) ("Meta-Circular Adventures in Functional Abstraction" "https://web.archive.org/web/20160609222905/https://chriskohlhepp.wordpress.com/metacircular-adventures-in-functional-abstraction-challenging-clojure-in-common-lisp/" ("common-lisp" @@ -113,6 +149,9 @@ ("Models of Generics and Metaprogramming: Go, Rust, Swift, D and More" "http://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/" ("c" "c++" "go" "optimization" "programming" "programming-languages" "rust")) + ("John Carmack on Inlined Code" + "http://number-none.com/blow/john_carmack_on_inlined_code.html" + ("c++" "optimization" "programming")) ("Notes on a smaller Rust" "https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/" ("programming" "rust")) @@ -126,9 +165,37 @@ "programming" "rust" "video-games")) + ("Creating a Rootkit to Learn C" + "https://h0mbre.github.io/Learn-C-By-Creating-A-Rootkit/#" + ("c" "security" "linux")) ("Building a Homebrew Video Game Console" "https://internalregister.github.io/2019/03/14/Homebrew-Console.html" ("c" "hardware" "programming" "z80")) + ("Approximating the Sine Function" + "http://datagenetics.com/blog/july12019/index.html" + ("math" "optimization")) + ("Looping Music Seamlessly" + "http://nolannicholson.com/looper.html" + ("math")) + ("The PGP Problem" + "https://latacora.micro.blog/2019/07/16/the-pgp-problem.html" + ("crypto" "math" "pgp" "security") + ((p "While I feel the author's suggestion to \"[u]se Signal. Or Wire, or +WhatsApp, or some other Signal-protocol-based secure messenger\" is +well-intentioned, I strongly dislike advocating for nonfree software and network +services that are run unethically. I am a proponent of OMEMO/XMPP.") + (p "I may start using signify for my own software. Of course, I won't +entirely give up on PGP, but I think that signing with both would be enough to +signal my endorsement of the former.") + (p "The lack of a suggested alternative for email is particularly depressing +to me. Why haven't we come up with a better system yet?") + (p "And the author does well to admit that PGP is still a fair choice for +encrypting files. I see myself continuing to use " + ,(hyperlink "https://www.passwordstore.org/" "pass") + " for years to come."))) + ("Infectious Executable Stacks" + "https://nullprogram.com/blog/2019/11/15/" + ("binary-exploitation" "c" "security")) ("In Noisy Digital Era, Elegant Internet Still Thrives" "https://www.npr.org/2012/04/17/150817325/in-noisy-digital-era-elegant-internet-still-thrives" ("culture")) @@ -138,6 +205,9 @@ ("Graying Out" "https://www.tbray.org/ongoing/When/201x/2019/03/11/Lights-Going-Out" ("culture" "jabber")) + ("What's Wrong With XMPP?" + "https://blog.samwhited.com/2019/02/whats-wrong-with-xmpp/" + ("culture" "jabber")) ("Does Visual Studio Rot the Mind?" "http://www.charlespetzold.com/etc/DoesVisualStudioRotTheMind.html" ("practices" "programming")) diff --git a/haunt/haunt.scm b/haunt/haunt.scm index 8a975ec..e47090c 100644 --- a/haunt/haunt.scm +++ b/haunt/haunt.scm @@ -47,7 +47,7 @@ (list (atom-feed) (blog) (blogroll) - (htaccess #:handler-404 "pages/404.html") + (htaccess #:error-documents '((404 . "pages/404.html"))) (outbox) (static-pages) (static-directory "static"))) diff --git a/haunt/jakob/builder/blog.scm b/haunt/jakob/builder/blog.scm index 93eb519..0eda775 100644 --- a/haunt/jakob/builder/blog.scm +++ b/haunt/jakob/builder/blog.scm @@ -90,7 +90,7 @@ (p ,@(maybe-cons* (date->string (post-date post) "~B ~d, ~Y") (when crosspost-uri - (list " ⮀ " (hyperlink local-uri "Crosspost"))) + (list " ↻ " (hyperlink local-uri "Crosspost"))) " ❖ Tags: " (intersperse (map (lambda (tag) @@ -98,7 +98,7 @@ (post-ref post 'tags)) ", "))) (p ,(first-paragraph post)) - ,(hyperlink (or crosspost-uri local-uri) "read more 🢩")))) + ,(hyperlink (or crosspost-uri local-uri) "read more →")))) ;;; @@ -139,7 +139,7 @@ only the posts tagged with that tag." (flat-map (match-lambda ((tag . posts) (items->pages render-preview posts - (format #f "Posts tagged as \"~a\"" tag) + (format #f "Posts tagged with \"~a\"" tag) (tag-uri %tag-prefix tag "")))) (group-by-tag posts (cut post-ref <> 'tags)))) diff --git a/haunt/jakob/builder/blogroll.scm b/haunt/jakob/builder/blogroll.scm index 70ab1f9..9d75c1e 100644 --- a/haunt/jakob/builder/blogroll.scm +++ b/haunt/jakob/builder/blogroll.scm @@ -24,6 +24,7 @@ #:use-module (jakob utils sxml) #:use-module (jakob utils tags) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) #:use-module (srfi srfi-26) #:export (blogroll)) @@ -36,19 +37,39 @@ ;;; -;;; Page creation. +;;; Type for entries. ;;; -(define (render-preview name uri tags tag-prefix) +(define-record-type <entry> + (make-entry name uri tags comments) + entry? + (name entry-name) + (uri entry-uri) + (tags entry-tags) + (comments entry-comments)) + +(define entry + (match-lambda + ((name uri tags) (make-entry name uri tags #f)) + ((name uri tags comments) (make-entry name uri tags comments)))) + + +;;; +;;; Rendering. +;;; + +(define* (render-preview name uri tags tag-prefix #:optional comments) "Return an SHTML preview of an entry with the given parameters." `(section - (h2 ,(hyperlink uri name)) - (p - ,(intersperse - (map (lambda (tag) - (hyperlink (tag-uri tag-prefix tag) tag)) - tags) - ", ")))) + ,@(cons* + `(h2 ,(hyperlink uri name)) + `(p + ,(intersperse + (map (lambda (tag) + (hyperlink (tag-uri tag-prefix tag) tag)) + tags) + ", ")) + (or comments '())))) (define (render-tag-cloud prefix entries) "Return SHTML listing the tags of ENTRIES in PREFIX with the number of times @@ -58,22 +79,25 @@ each tag is used." ((tag count) (hyperlink (tag-uri prefix tag) `(li ,(format #f "~a (~a)" tag count))))) - (count-tags entries third)))) + (count-tags entries entry-tags)))) (define* (render-entries title prefix entries #:optional tag) "Return an SHTML document listing ENTRIES in PREFIX, with a header of TITLE." `(main ,@(maybe-cons* `(h1 ,(if tag - (format #f "~a - Tagged as ~a" title tag) + (format #f "~a - Tagged with \"~a\"" title tag) title)) (unless tag (render-tag-cloud prefix entries)) (unless tag `(hr)) - (map (match-lambda - ((name uri tags) - (render-preview name uri tags prefix))) + (map (lambda (entry) + (render-preview (entry-name entry) + (entry-uri entry) + (entry-tags entry) + prefix + (entry-comments entry))) entries)))) (define (entries->pages title prefix entries) @@ -90,7 +114,7 @@ pages for each of the tags used in ENTRIES." (theme #:title title #:content (render-entries title prefix entries tag)) sxml->html))) - (group-by-tag entries third)))) + (group-by-tag entries entry-tags)))) @@ -101,12 +125,12 @@ pages for each of the tags used in ENTRIES." (define %blogroll (list "Blogroll" "/blogroll" - (primitive-load "data/blogroll.scm"))) + (map entry (primitive-load "data/blogroll.scm")))) (define %bookmarks (list "Bookmarks" "/bookmark" - (primitive-load "data/bookmarks.scm"))) + (map entry (primitive-load "data/bookmarks.scm")))) (define (blogroll) (lambda (site posts) diff --git a/haunt/jakob/builder/htaccess.scm b/haunt/jakob/builder/htaccess.scm index fd20eca..e0068d1 100644 --- a/haunt/jakob/builder/htaccess.scm +++ b/haunt/jakob/builder/htaccess.scm @@ -16,20 +16,35 @@ (define-module (jakob builder htaccess) #:use-module (haunt page) + #:use-module (ice-9 match) #:export (htaccess)) +;; Good resource: +;; <https://perishablepress.com/stupid-htaccess-tricks/#ess4> + (define* (htaccess-writer contents #:optional (port (current-output-port))) (display (string-join contents "\n") port) (newline port)) -;; TODO: https://help.dreamhost.com/hc/en-us/articles/215747748-How-can-I-redirect-and-rewrite-my-URLs-with-an-htaccess-file- -(define* (htaccess #:key handler-404) +(define* (htaccess #:key + (error-documents '()) + (redirects '())) "Create an .htaccess file at the site's root. -HANDLER-404 specifies the file name of the page to display for a 404 not found." +ERROR-DOCUMENTS specifies the file name of the page to display for a specific +HTTP error code: a list of (error-code . file-name) pairs. + +REDIRECTS specifies file names to show for certain requests: a list of (pattern +. file-name) pairs." (define contents - `(,(if handler-404 - (format #f "ErrorDocument 404 ~a" handler-404)))) + `(,@(map (match-lambda + ((code . file-name) + (format #f "ErrorDocument ~a ~a" code file-name))) + error-documents) + ,@(map (match-lambda + ((pattern . file-name) + (format #f "RewriteRule ~a ~a" pattern file-name))) + redirects))) (lambda (site posts) (make-page ".htaccess" contents htaccess-writer))) diff --git a/haunt/jakob/utils/pagination.scm b/haunt/jakob/utils/pagination.scm index a012dd4..2cda3c7 100644 --- a/haunt/jakob/utils/pagination.scm +++ b/haunt/jakob/utils/pagination.scm @@ -68,9 +68,9 @@ PREVIOUS-PAGE and NEXT-PAGE." (@ (id "pagination")) ,@(maybe-list (when previous-page - (hyperlink previous-page "🢨 Previous Page")) + (hyperlink previous-page "← Previous Page")) (when next-page - (hyperlink next-page "Next Page 🢩")))))) + (hyperlink next-page "Next Page →")))))) (define* (items->pages render-item items base-title base-file-name #:key (items-per-page %items-per-page)) diff --git a/haunt/pages/404.sxml b/haunt/pages/404.sxml new file mode 100644 index 0000000..450a612 --- /dev/null +++ b/haunt/pages/404.sxml @@ -0,0 +1,19 @@ +;;; -*- scheme -*- + +;;; Copyright © 2019 Jakob L. Kreuze <zerodaysfordays@sdf.lonestar.org> +;;; +;;; This program is free software; you can redistribute it and/or +;;; modify it under the terms of the GNU General Public License as +;;; published by the Free Software Foundation; either version 3 of the +;;; License, or (at your option) any later version. +;;; +;;; This program is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;; General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program. If not, see +;;; <http://www.gnu.org/licenses/>. + +`(p "Test") diff --git a/haunt/pages/about.sxml b/haunt/pages/about.sxml index 603d3e8..911bf8c 100644 --- a/haunt/pages/about.sxml +++ b/haunt/pages/about.sxml @@ -16,17 +16,115 @@ ;;; along with this program. If not, see ;;; <http://www.gnu.org/licenses/>. -(use-modules (jakob theme) +(use-modules (ice-9 match) + (jakob theme) (jakob utils sxml)) +(define %software-projects + '(("Mines" "Dormant" + "https://git.sr.ht/~jakob/mines" + ("Android implementation of the classic video game \"Minesweeper\".")) + ("Rebuild" "Dormant" + "https://git.sr.ht/~jakob/rebuild" + ("An attempt at reimplementing Ken Silverman's Build engine, with the goal of being modular enough to host a modern Blood source port.")) + ("wildmidi" "Dormant" + "https://git.sr.ht/~jakob/wildmidi" + ("A simple [Rust] wrapper around the WildMIDI software synthesizer library.")) + ("Slime the World" "Finished" + "https://git.sr.ht/~jakob/slime-the-world" + ("My entry for the 2018 Autumn Lisp Game Jam; a game about covering everything in slime.")) + ("Nekopack" "Finished" + "https://git.sr.ht/~jakob/nekopack" + ("Tool for extracting game data from Nekopara's XP3 archives.")) + ("skullfuck" "Finished" + "https://git.sr.ht/~jakob/skullfuck" + ("Optimizing compiler for Brainfuck, contained in a single C source file.")) + ("Brisket" "Abandoned" + "https://git.sr.ht/~jakob/brisket" + ("Primitive newsreader script for interacting with NNTP servers.")) + ("Hypodermic" "Abandoned" + "https://git.sr.ht/~jakob/hypodermic" + ("Proof-of-concept shared object injector that will eventually be integrated with PINCE.")) + ("Duke on FluidSynth" "Abandoned" + "https://git.sr.ht/~jakob/duke-on-fluidsynth" + ("Experimental FluidSynth MIDI driver for EDuke32.")))) + +(define %emacs-projects + '(("dired-rmjunk" "Active" + "https://melpa.org/#/dired-rmjunk" + ("A port of Jakub Klinkovský's rmshit.py to Dired.")) + ("ox-haunt" "Active" + "https://melpa.org/#/dired-rmjunk" + ("Haunt-flavored HTML backend for the Org export engine.")))) + +(define %software-contributions + `(("GNU Guix" "Active" + "https://guix.gnu.org" + ("Functional package manager.")) + ("Radare2" "Finished" + "https://www.radare.org/r/" + ("UNIX-like reverse engineering framework and command-line tools.")) + ("Haiku" "Finished" + "https://www.haiku-os.org/" + ("The Haiku operating system.")))) + +(define %packaging-projects + '(("zerodaysfordays" "Dormant" + "https://git.sr.ht/~jakob/zerodaysfordays" + ("My personal Gentoo ebuild overlay.")))) + +(define (project name status url description) + (let ((status-class (match status + ("Active" "good") + ("Finished" "info") + ("Dormant" "okay") + ("Abandoned" "bad") + (_ "info")))) + `(tr (td ,(hyperlink url name)) + (td (button (@ (class ,(format #f "label-~a" status-class))) + ,status)) + (td (p ,@description))))) + +(define (project-section name projects) + `(section + (h4 ,name) + (table + (tbody + ,@(map (lambda (args) (apply project args)) projects))))) + (theme #:title "About" #:content `((h2 "Hey.") - (p "I'm still in the process of putting things together, so this page is a bit empty.") - (p "For those interested in contacting me, here's my info.") - (ul - (li "Email: zerodaysfordays at sdf.lonestar.org") - (li "GPG: " ,(hyperlink "5A7B 8C4C 70D5 FC3F 5E35 4625 3FE7 8039 D0E5 C118" "/gpg.txt")) - (li "XMPP: jakob at xmpp.is") - (li "IRC: tsarfox on freenode.net, and jakob everywhere else. I frequent #lobsters and the #tildetown on the tilde.town internal IRC.")))) + (ul (@ (class "horizontal-list")) + (li "Email: " (strong "zerodaysfordays at sdf.lonestar.org")) + (li "XMPP: " (strong "jakob at xmpp.is")) + (li "IRC: " (strong "tsarfox on freenode.net."))) + (ul (@ (class "horizontal-list")) + (li "GPG: " ,(hyperlink "/static/gpg.txt" "5A7B 8C4C 70D5 FC3F 5E35 4625 3FE7 8039 D0E5 C118"))) + (ul (@ (class "horizontal-list")) + (li ,(hyperlink "https://git.sr.ht/~jakob" "git")) + (li ,(hyperlink "https://mastodon.sdf.org/@jakob" "fedi")) + (li ,(hyperlink "https://en.wikipedia.org/wiki/User:ZeroDaysForDays" "wiki")) + (li ,(hyperlink "http://tilde.town/~jakob" "tilde")) + (li ,(hyperlink "http://zerodaysfordays.sdf.org/" "sdf")) + (li ,(hyperlink "https://lobste.rs/u/jakob" "lobste.rs"))) + (main (@ (class "overline")) + (p "My name's Jakob. I'm a self-taught free software hacker currently +studying computer science and mathematics at the University of Massachusetts +Amherst. I'm into functional programming, computer security, permaculture, +cooking, punk rock, and lifting weights.") + (p "I'm still in the process of putting things together, so this page is a bit empty.")) + (h3 "Site Map") + (nav (@ (id "site-map")) + (section (h4 ,(hyperlink "/blogroll" "Blogroll")) + (p "A noncomprehensive list of the blogs I follow.")) + (section (h4 ,(hyperlink "/bookmark" "Bookmarks")) + (p "Collection of some things that I especially enjoyed reading.")) + (section (h4 ,(hyperlink "/outbox" "Webmention Outbox")) + (p "List of all the Webmention replies I've sent. Probably uninteresting to most."))) + (h3 "Projects") + ,(project-section "Software" %software-projects) + ,(project-section "Emacs" %emacs-projects) + ,(project-section "Packaging" %packaging-projects) + ,(project-section "Other Contributions" %software-contributions))) diff --git a/haunt/static/css/style.css b/haunt/static/css/style.css index 02001ec..6cd9656 100644 --- a/haunt/static/css/style.css +++ b/haunt/static/css/style.css @@ -73,6 +73,20 @@ body > header > nav > ul { } } +/* Horizontal lists. */ + +.horizontal-list { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + + list-style-type: none; + text-align: center; + + margin: 0; + padding: 8px 0; +} + /* Pagination. */ #pagination { @@ -209,3 +223,83 @@ body > footer > div > p { margin: 0; padding-right: 1rem; } + +/* About page. */ + +#site-map { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 1rem; + + padding: 0 1rem; +} + +#site-map > section { + text-align: center; +} + +.overline { + border-top: 2px solid #d2d6dd; + margin-top: 0.5rem; +} + +/* Tables. */ + +table { + text-align: center; + width: 100%; +} + +td { + padding: 0 1rem; +} + +tr:nth-child(even) { + background-color: #f2f2f2; +} + + +td:nth-child(1) { + width: 20%; +} + +td:nth-child(2) { + width: 10%; +} + +td:nth-child(3) { + width: 70%; +} + +/* Buttons. */ + +button { + display: inline; + padding: .3em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} + +.label-info { + background-color: #5bc0de; +} + +.label-good { + background-color: #5cb85c; + border-color: #4cae4c; +} + +.label-okay { + background-color: #ec971f; + border-color: #d58512; +} + +.label-bad { + background-color: #dc3545; +} diff --git a/haunt/static/gpg.txt b/haunt/static/gpg.txt new file mode 100644 index 0000000..aaa2630 --- /dev/null +++ b/haunt/static/gpg.txt @@ -0,0 +1,190 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFyL0CEBEACrmHInpe3ZUJ5f0lhgYbvVobjBaSgu6XKt1Lm640XR/d5RmCsh +RS2oJ0zghzRQMx+BBTxr62OmuI8FU9MfU+ySHNJWcyobmGaPH0VG3n6xlkBY9g7l +zm+lccrxpXTd8QH5KRQNUYcL7/aYRNgtqYrYWmkxPSTM2ydFHc+W9wEAZTHLOQsc +aQJold5KJHM7DTwOq21oltLwWKCOL+MfMejjYoYRMT0UE9DduyR1H/cKsF9VvNjV +AHFaxCcibtYK0s5jyq/fVIq8ZqU9AEnwUtspqVzWINaDCbVPL/NzFkjDlzu5Y9Bi +ff9B/Ncd5TmstnsJ9cj6IgYjtwEPLw+aSJNsuzLlQROPDqDM6gaq8v+q8cjk2EH0 +ft0uFlWENf3kfql/WMMn8iVtYon0h9LLI480dQix4+OjNQFVrryPstuA2wTvGqIH +ECYJeI5hLT5EdShHEXGQEE5JAqXhd19lrWfoV0HI7IKnxE2UI+riaUNSJE8VCmIm +Y/e2YU/YFIzbrGOh/q0M2te+4ATrexwyBAxrlzl8gFiM3vTQGwJSIeIIlcVoUbti +/UcywO9pKQlga7AN0oQ0GnmWkGmdLj44wFFuIQSDdkGDyxurh+RPspnSW4HAz3fC +IbC5h9ACGcLWYT3p7JZGiR9SRbAHBDTgBF5F3hV9ORJpZ8ykN1V9iMq9pwARAQAB +tEdKYWtvYiBMLiBLcmV1emUgKGh0dHA6Ly9qYWtvYi5zcGFjZSkgPHplcm9kYXlz +Zm9yZGF5c0BzZGYubG9uZXN0YXIub3JnPokCUwQTAQgAPQIbAwUJAeEzgAIeAQIX +gBYhBMr8F0Od9PwacViMZSdn1f7sIYnvBQJci9d+BQsJCAcCBhUKCQgLAgMWAgEA +CgkQJ2fV/uwhie9HqA//SY9dIX46hjNIQpvWWLq8yKKMPZeC1B81JwyK3vimPrNz +MWeMTJcnmzupNfHXtSaYGh3GlBj6sRONLEGQVS25qBOLgo5TOtPD4V/F4rbqmwRN +scIMmHPN7s/n60if7dF2bOmrasCpkhROj8CCTsaImbFCYQW5kOywkvxP3tPM6sJd +6yIqMeb/LR+7VOYYZuCGx3VTXmLdB4eMtY7fHE3K9iZp4yNYsxib+mgeIFPhsdQ2 +uGC7aGm9XusKVLMx4zGrm0ddZdsQyGosp6Ynd4t+5Xb3PM6UyvTNVwTxh6vEBIMb +yIdz/6tC53xw0RlA86FUH+IY3CQLECjbZDPsjmKkD7KNcckUk11eQkhgMDHxgZAl +v3Qe4qiDb89UdVpkBJJvxjJztgVyjfHwJdsErzktgOZpnBP/TxNlKwmvKaffMe35 +kIzl0RHrQffQlLhWaw6nndsWzD33jqyd6LzVxhLP+s5QKFIC7KN63d0BeUqEQirD +Z6t1oKSaRWbK67MRRdTQ/hPb2wthw1TQ0ANXWMcpOHyyyZj2F64PLT6sWXvaE9Ky +PAUZP2StHWztz6sGyshmCWQot2gROUxbVqyRLxKu158BSbqKbjf2DuwTju8Vtpcn +qXJDE7L9LvuJo79CmGR/4cGN8SVY/yEfXPQh8+tqHJ0asXQ/iJ2pVZk5up9Wnr7R +0DbQNAEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3Jl +YXRlZCB3aXRoIEdJTVD/2wEEEAA1ADUANQA1ADkANQA8AEMAQwA8AFMAWgBQAFoA +UwB7AHEAZwBnAHEAewC6AIUAjwCFAI8AhQC6ARsAsADOALAAsADOALABGwD6AS8A +9gDmAPYBLwD6AcIBYQE5ATkBYQHCAgcBtAGdAbQCBwJ1AjMCMwJ1AxkC8QMZBAwE +DAVwEQA1ADUANQA1ADkANQA8AEMAQwA8AFMAWgBQAFoAUwB7AHEAZwBnAHEAewC6 +AIUAjwCFAI8AhQC6ARsAsADOALAAsADOALABGwD6AS8A9gDmAPYBLwD6AcIBYQE5 +ATkBYQHCAgcBtAGdAbQCBwJ1AjMCMwJ1AxkC8QMZBAwEDAVw/8IAEQgBIADwAwEi +AAIRAQMRAf/EABoAAAIDAQEAAAAAAAAAAAAAAAAEAgMFAQb/2gAIAQEAAAAA0CYA +AcVbAyuMq0t2dDgJnHXgAELWgyS9K1kAAI3ClWvMCvB3rDK5eg3aAABRfZSJ6LHF +kObpmVsJM2gAAEOMKFiseswacz12V+NAAABXW5VUdhB1LZWzm4o6IAAAC0C+htS/ +i+vZiuxVtnYAAACN0Zyu5SvrXU5jUUY6EwAAAz5aE6Vhp3pTmNVZ12iRkAAAx3L2 +OgAV5DlC97NfLSidgAU2r6F4ABhPRRemm1Kii1kAL4UWuAAGI5zO0u52jXS9mvAB +nxnptAAGM1FDSM3Rz3OoaYBzPvT9DOFdMSdyE+5+kZ17rgotU2cV0cxdzhbcEIUw +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 +BC2J8VM1ySLIhdpwOpBmfBui8bkX1s2dedJ+XQ== +=c2uQ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/org/A Reflection on New Ambitions for a New Year/new-ambitions-for-a-new-year.org b/org/A Reflection on New Ambitions for a New Year/new-ambitions-for-a-new-year.org deleted file mode 100644 index efa5015..0000000 --- a/org/A Reflection on New Ambitions for a New Year/new-ambitions-for-a-new-year.org +++ /dev/null @@ -1,279 +0,0 @@ -#+TITLE: A Reflection on "New Ambitions for a New Year" -#+DATE: <2019-03-03 Sun> -#+TAGS: writeup, practices - -On the night of December 31st of last year, I wrote "New Ambitions for a New -Year," a blog post outlining a number of self-imposed goals for 2019. The words -I ended on were: "[t]oo ambitious? Well, I guess we'll find out in 365 days when -I write another post like this." Indeed, what you're reading right now is -something I expected to author much later, but enough has changed in the past -three months that I feel it's appropriate to put out an addendum now. The post's -original text follows. - ---- - -As we draw on the final hours of 2018, the only words I feel appropriate are: -"wait, it's really January tomorrow?" The past twelve months have felt -deceivingly short despite everything that's happened in that span of time. I had -a number of rough job interviews, met Richard Stallman in person, installed -Gentoo, became a legal adult, graduated high school, and finished my first -semester of university, all in what seemed to me like just a few weeks. - -"Productive" is how I'd characterize the year, but I'm having a hard time -rationalizing why it is that I feel that way. Yes, I reached two major -milestones in my education, but I still don't know what to make of everything -else I did. I'd probably feel better about my accomplishments if I had a goal -and a means of assessing whether or not I achieved it, but alas, I didn't set -any out of the same cynicism that drives me to point out resolutioners -contributing to the January gympocalypse. I'd like to mend that cynicism for -2019, though. One year really isn't a bad span of time for setting goals -- it's -long enough that you can be ambitious in your planning, but short enough that -there's pressure to continue making progress throughout the year. - -So, I've come up with some goals for myself, and I've decided to write about -them to impose some level of accountability. - -* Doing - -** Writing - -This year, I managed to put out seven articles. Last year saw four, and the year -before saw one. A linear regression suggests that I'll be putting out ten this -year. - -I kid. As tempting as it is to make "write more frequently" one of my goals, my -focus will be on quality over quantity. In 2019, I plan to: - -- Read through /all/ of my past posts, drafts, and [[http://jakob.space/links/][favorite articles]], taking - notes on how they're written, and synthesizing a writing style that can be - thoroughly described on paper. If you've read my earlier writing, I anticipate - that you've noticed significant variance in the presentation of each piece. - It's time for me to identify the aspects of style that work, and find a way to - consistently incorporate those aspects into my future writing. -- In a similar fashion, develop a consistent method for doing research and - managing sources throughout the writing process. I believe my technical - writing will benefit if I make a habit of perusing the existing corpus of - research prior to putting words down on paper. -- Expand my writing endeavors to Wikipedia. I've collected a number of sources - for expanding Wikipedia stubs, and my goal is to turn every entry on that list - into a series of edits. Ideally, I'll go beyond that, but given the preceding - goals, I think that it makes sense to start small. Writing for Wikipedia will - expose me to critique from more experienced writers, and give me experience - writing in an encyclopedic style. - -No hard article count goals this year, because again, quality over quantity. If -I'm forcing myself to write to fill a quota, my heart won't be in it. - -** Contributing to Free Software - -I'd like to migrate all of the projects I maintain to [[https://git.sr.ht/~jakob/][sr.ht]], but I'll be keeping -my GitHub account for sending pull requests to projects that live on there. - -Originally, I had a list of projects that I wanted to contribute to in the new -year, but I haven't read a single line of code from any of the entries on that -list and there's a good chance that I'll have second thoughts about contributing -following the first perusals, so I've decided that a more reasonable goal is to -become a regular contributor to at least one project. By "regular contributor," -I mean gaining enough of a presence that people are coming to me to ask -questions about the code. - -* Learning - -** Computer Science - -I have tried, and failed, numerous times to get through Hal Abelson and Gerald -Sussman's /Structure and Interpretation of Computer Programs/. Now that I'm more -mature in terms of rigor, I've decided to finally buckle down to make my way -through the text and all of its exercises. It's a fat 657 page book, but I'm -more than capable, and I have an entire year to do it. - -My more lighthearted goal is to put out at least one demo on either the Super -Nintendo or the Nintendo DS. I've always wanted to program for a platform where -I'm constrained by the limitations of the hardware. - -** Computer Security - -Capture-the-flag used to be a more significant hobby of mine; I mostly dropped -off the scene when I graduated high school, but I'd like to get back into it. In -a similar vein to my SICP goal, I'm aiming to finally get through a book that -I've failed to finish in the past, [[https://beginners.re/][Reverse Engineering for Beginners]], and -complete _at least_ 50 of its exercises. 50 is a about one per week, which I -think is reasonable. - -** Electronics - -I've tried several times in the past to learn "electronics", and never -progressed beyond the laws of Ohm and Joule. I think the issue was that I didn't -know what I wanted to learn; I set out to study "electronics," which wasn't -specific enough. This time, I'll be more diligent in finding curriculum that's -relevant to me. I want to learn to read boards and gain the knowledge necessary -to repair my Commodore 64, so my focus will be on the design of electronic -circuits. I'll achieve this is by going through the coursework for MIT's 6.002: -"Circuits and Electronics". It's a course I wanted to go through when I was a -sophomore in high school, but at the time, I was discouraged by the list of -prerequisites in the syllabus. - -Another goal of mine is to join an amateur radio club. I've already got two in -mind: [[http://sdfarc.org/][SDFARC]] and the one that runs the local 2-meter repeater I frequent. My -thought is that joining one will give me an opportunity to find an Elmer whose -brain I can pick about RF electronics. - -** Mandarin Chinese - -One of my favorite courses in high school was Mandarin Chinese. I took it for -four years, and while I wanted to continue studying it at university, I sadly -couldn't fit any classes into my schedule. The four years of study left me with -a grasp of the language's fundamentals, however, and I think that I could -reasonably continue to learn the language on my own accords. Throughout the -year, I plan to: - -- Learn the vocabulary on [[https://en.wiktionary.org/wiki/Appendix:Mandarin_Frequency_lists/1-1000][Wiktionary's list]] of the 1000 most frequently used - words in Mandarin. I already know a number of words on the list from my four - years of study, and learning the rest should give me a good base for reading - from newspaper articles and books. I plan to learn the words' definitions and - pronunciations through spaced repetition, and I'll handwrite the character - every time I review the card for it. Although the ability to handwrite - characters isn't a specific goal of mine, I've found that it helps me in - learning to recognize the characters. -- Read /C程序设计语言/. I have a translated copy of Kernighan and Ritchie from - when I went to 王府井, so that should be reading that's both engaging and full - of the kind of vocabulary that I'm interested in learning. -- Make friends with at least one of the international student in my dorm. I'd - feel bad conscripting someone solely for the purpose of helping me learn - Mandarin, but I figure that if I befriend someone whose native tongue is - Mandarin and spit a bunch of broken Chinese at them, they'll be inclined to - correct me (through laughter, perhaps). - -To recap, my goals this year are: - -- Move my software projects to [[https://sr.ht/][sr.ht]]. -- Join an amateur radio club. -- Make friends with at least one of the international students in my dorm. -- Synthesize a consistent writing style and document it. -- Synthesize a method for performing research and document it. -- Turn my list of articles into a series of edits to Wikipedia stubs. -- Learn the 1000 most frequently used words in Mandarin Chinese. -- Read /C程序设计语言/. -- Read /SICP/ and complete all of its exercises. -- Read /RE4B/ and compete at least 50 of its exercises. -- Complete the coursework for 6.002. -- Put out at least one demo on either the Super Nintendo or the Nintendo DS. -- Become a regular contributor to at least one free software project. - -Too ambitious? Well, I guess we'll find out in 365 days when I write another -post like this. - ---- - -As alluded to in the introduction, I've come to understand that achieving every -goal on that list within the span of a year is impossible. Well, at least for a -full-time university student. Balancing a course load of 19 credits with -undergraduate research and a social life leaves me with little more than a few -hours on the weekends to dedicate to those tasks. As such, I'm reevaluating and -redefining my goals for 2019. - -What's more, some of the things on that list no longer even interest me. Why -invest significant time into something I know I won't find fulfilling? Take the -plan for continuing my study of Mandarin Chinese as an example: bilingualism -seems like it might be useful, but I have no realistic use for the skill, and I -was really dreading the daily practice I was putting in for the first few weeks -of the year. - -The other issue that's come up is a loss of motivation for some of the tasks -that, unlike continuing my study of Mandarin, I /would/ find fulfilling. To remedy -this, I've decided to associate small integrative projects with each of the -goals I intend on keeping. This move is inspired by the points made in Norvig's -[[http://norvig.com/21-days.html][Teach Yourself Programming in Ten Years]], which I had read several years ago but -was reminded of after a recent perusal of the introduction to [[http://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/][Functional -Programming in OCaml]]. I'd specifically like to quote a few things he lists in -his "recipe for programming success": - -- Get *interested* in programming, and do some because it is fun. Make sure that - it keeps being enough fun so that you will be willing to put in your ten - years/10,000 hours. -- *Program*. The best kind of learning is learning by doing. To put it more - technically, "the maximal level of performance for individuals in a given - domain is not attained automatically as a function of extended experience, but - the level of performance can be increased even by highly experienced - individuals as a result of deliberate efforts to improve." (p. 366) and "the - most effective learning requires a well-defined task with an appropriate - difficulty level for the particular individual, informative feedback, and - opportunities for repetition and corrections of errors." (p. 20-21) The book - Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life is an - interesting reference for this viewpoint. -- *Talk with* other programmers; read other programs. This is more important than - any book or training course. -- Work on *projects with* other programmers. Be the best programmer on some - projects; be the worst on some others. When you're the best, you get to test - your abilities to lead a project, and to inspire others with your vision. When - you're the worst, you learn what the masters do, and you learn what they don't - like to do (because they make you do it for them). -- Work on *projects after* other programmers. Understand a program written by - someone else. See what it takes to understand and fix it when the original - programmers are not around. Think about how to design your programs to make it - easier for those who will maintain them after you. - -Working towards a project is largely conducive of these. It will ensure that I'm -having fun, that I'm learning by doing, and that I'm speaking with those who -have expertise. So, here's my updated list of goals for 2019, paired with the -projects I've come up with: - -- Complete the coursework for MIT's 6.002. - - Troubleshoot my Commodore 64 and restore it to a working state. -- Read chapters 1-4 of Structure and Interpretation of Computer Programs and - complete all of their exercises. - - Implement a ray-tracer in Scheme. -- Compete 50 of the exercises in Reverse Engineering for Beginners. - - Write another game hacking post. -- Synthesize a consistent writing style and document it. - - Write another game hacking post. -- Synthesize a method for performing research and document it. - - Adopt the "Heap feng shui" and "JIT spraying" pages on Wikipedia. - -Although this is still an ambitious list, I think I can reasonably achieve all -of these by 2020. I think the more important point, however, is that I've -effectively reduced my original ragtag list of last-minute New Year's Eve -thoughts into a clear plan for personal growth. Completing the coursework for -6.002 will enable me to design and reverse engineer circuits. Completing -Structure and Interpretation of Computer Programs will teach me to approach -problems with a functional programming mindset. Working through the exercises -from Reverse Engineering for Beginners will give me a repertoire of patterns for -going forward in capture-the-flag and any projects I take on in the future that -may involve reverse engineering. Developing a consistent writing style will -strengthen my ability to communicate knowledge, and developing a consistent -method for performing research will encourage me to exercise that skill of -communicating knowledge. - -During my initial reflection, I thought that troubleshooting the C64 would make -for a poor choice of project, as there's the possibility that I'd start -troubleshooting and soon realize that it's borked beyond repair. One thought was -to instead follow the instructions outlined in Steve Ciarcia's Build Your Own -Z80 Computer, but after looking through the book, I realized that it would be -both expensive and probably very difficult to complete due to the age of the -components used.[fn:1] I also thought to follow Ben Eater's instructions for -[[https://eater.net/8bit/kits][building an 8-bit computer from scratch]], but that also has the issue of price. -Ultimately, I decided to go with my original plan to fix the C64. Even if I -can't repair the C64, I have a broken VIC-20 I can fall back on, and I think I'd -learn more in the domain of reverse engineering circuits than I would following -the instructions that come with some homebrew computer kit. - -My definition of success for the goal of reading Structure and Interpretation of -Computer Programs changed slightly to omit the fifth chapter. I still plan to -read it, just not this year. My reasoning comes mainly from skimming ahead to -get an idea for an integrative project and seeing exercise 5.51. It'd just be... -well, a lot. Why a ray-tracer? It's short, sweet, utilitarian, and would -probably be both pleasant and eye-opening to implement in a functional style. - -I'll still be working through the exercises in Reverse Engineering for -Beginners, but I will not be reading the book in it's entirety. After going -through the first few chapters, I've developed the opinion that the book makes -for incredible reference material, but that a page-by-page reading wouldn't be -valuable for someone with prior experience in reverse engineering. I'll still be -skimming a subset of the book[fn:2], but I don't want promise to myself that -I'll finish those chapters by the end of this year. - -One last thing to note -- I've omitted the goal about moving all my software -projects to sr.ht, as I've succeeded in doing that. - -[fn:1] How the hell am I going to get my hands on a CRT terminal in 2019? -[fn:2] Chapters 30-63, 68, 76, and 88. diff --git a/org/First Impressions of the Kotlin Programming Language/first-impressions-of-the-kotlin-programming-language.org b/org/First Impressions of the Kotlin Programming Language/first-impressions-of-the-kotlin-programming-language.org index 75b7438..742103b 100644 --- a/org/First Impressions of the Kotlin Programming Language/first-impressions-of-the-kotlin-programming-language.org +++ b/org/First Impressions of the Kotlin Programming Language/first-impressions-of-the-kotlin-programming-language.org @@ -2,7 +2,7 @@ #+DATE: <2018-12-17 Mon> #+TAGS: opinion, programming, java, kotlin, android -In the introduction of the previous post I wrote for this series, [[http://jakob.space/posts/first-impressions-of-the-rust-programming-language/][First +In the introduction of the previous post I wrote for this series, [[http://jakob.space/blog/first-impressions-of-the-rust-programming-language.html][First Impressions of the Rust Programming Language]], I alluded to the presence of arguments that programming language safety should be achieved by moving to languages such as Java which run on a virtual machine. While "safety" may no diff --git a/org/First Impressions of the Myrddin Programming Language/first-impressions-of-the-myrddin-programming-language.org b/org/First Impressions of the Myrddin Programming Language/first-impressions-of-the-myrddin-programming-language.org new file mode 100644 index 0000000..fec4a31 --- /dev/null +++ b/org/First Impressions of the Myrddin Programming Language/first-impressions-of-the-myrddin-programming-language.org @@ -0,0 +1,196 @@ +#+TITLE: First Impressions of the Myrddin Programming Language +#+TAGS: opinion, programming, myrddin + +# Man the memory management model of this lang is so wierd I'm constantly +# dealing with things being freed from under my feet. + +# Also, no shadowing, which sucks. + +# It's been a long time since I've made a post like this. + +I'm not sure there are many applications writen in Myrddin. The [[https://eigenstate.org/software/ircmyr][irc.myr]] IRC +client comes to mind, but not much else. It seems most of the work has gone +towards developing libraries. + +It took a restless night for me to come up with a good project to use Myrddin +for. The limited architecture support restricted my choices somewhat. I had +considered writing a very lightweight [[https://en.wikipedia.org/wiki/Imageboard#Danbooru-style_boards][Booru]] engine to use as a self-hosted, +tagged image gallery, but my "server" [fn:1] is ARMv6, so I wouldn't be getting +much use out of it. Ah, well. The next language I plan to write about is built +upon LLVM, so I'll be able to target my server then. I also toyed with the idea +of a text editor and a Lisp interpreter, but settled on a CHIP-8 emulator. +First, it would be a good opportunity to demonstrate the C binding support as +I'll be using SDL. There's plenty of room to go above the bare-minimum for +features, such as additionally implementing an assembler, debugger, or JIT. +Furthermore, it would be (to my knowledge) the first graphical application +writen in Myrddin. + +# Also, I figure having a CHIP-8 emulator of my own would be good, since it +# would be a fun target for a "toy" compiler if I decide to make one. + +# Mention the [[https://myrlang.org/wishlist][wishlist]] + +* Tooling + +# There's a syntax highlighting implementation for vim, but I don't use vim + +So I've written a [[https://git.sr.ht/~jakob/myrddin-mode][major mode]] for GNU Emacs. + +# It's packaged in melpa! + +* Build System + +The build system for Myrddin is interesting, and very reminiscent of + +- =mbld= +- =6m= is the actual compiler. +- =muse=??? + +# Error messages are AWFUL + +#+BEGIN_SRC prog +jakob@Epsilon ~/Code/chip $ mbld -b asm asm.myr && ./asm + 6m asm.myr +asm.myr:52: type "char" incompatible with "byte" near Otup:(union + `std.None + `std.Some byte[:] +;;,byte[:][:]) + char from asm.myr:24 + byte from asm.myr:8 +FAIL: 6m asm.myr +#+END_SRC + +# This one was in response to trying to do .len on an array rather than a slice + +#+BEGIN_SRC prog +jakob@Epsilon ~/Code/chip $ mbld -b asm asm.myr + 6m asm.myr +6m: typeinfo.c:363: tyoffset: Assertion `ty->type == Tystruct' failed. +CRASH: 6m asm.myr +#+END_SRC + +One interesting difference between Myrddin and Rust is that Myrddin does not +require that arguments have a type specifier. + +#+BEGIN_SRC myrddin +use std + +const factorial = {n + if n < 1 + -> 1 + ;; + -> n * factorial(n - 1) +} + +const main = { + std.put("Hello, world!\n") +} +#+END_SRC + +But as soon as you have more complicaed functions that aren't used... + +[fn:1] At the moment, a Raspberry Pi B+. It does the job, but I'm afraid of blowing the 512MB RAM trying to run a full MediaGoblin instance on it. + +# --- + +- Entry point is the function named =main=. +- Simpler, but similar to Rust's format specifies for output (=std.put("{} + {} = {}\n", 2, 2, 5)=) +- Function prototypes not necessary (!) + - Drew Devault's recent mockup requires function prototypes. +- Lines aren't terminated with semicolons (feels a bit like Go?) + +- Declarations begin with =var=, =const=, or =generic=. + - Specify type like in rust (=var acc: int=) + - There's type inference (cool!) + +- There's no special syntax for function declarations; functions are declared by + assigning a function literal to a constant. + - A bit like Scheme, I guess? + - Function literal syntax is a bit weird, though. + +#+BEGIN_SRC myrddin +{arg, list + function + body +} +#+END_SRC + +- Argument types aren't actually needed (unlike in Rust). +- Fixed-width integer types (unlike C, more like Rust, which I really appreciate) + +- Multiple exit points for a function, i.e. + +#+BEGIN_SRC myrddin +const factorial = {n + if n < 1 + -> 1 + ;; + -> n * factorial(n - 1) +} +#+END_SRC + + - Minor point, but returning early from a function in Common Lisp is a pain in + the rear. (Of course =if= being an expression makes =factorial= trivial to + implement without it). + +- Only control flow is =if/elif/else=, =while=, =for=. + - I like =elif=, like Python. + +- Pattern matching!!! + - "Pattern matches can descend into the structure of almost any type. + Structures, arrays, strings, unions, and even values on the other end of + pointers are fair game." + - There's also algebraic data types, which I'm diggin'. + - The backtick syntax is nice for readability imo. + - =std.result= + +- Generics. Dunno how to feel about that, yet. + - There are traits, which I'm not comfortable enough with in Rust to make a + comparison. + - Lends itself to allowing for iterators to be implemented. + +- Libraries are so simple. Just a =pkg= declaration. +- Array literals are pretty cool. + - =x = [0: 1, 73: 2]=. +- Structs are literals, assigned to a =type= declaration. + +- Operators are pretty much the same as C, which makes the learning curve quite nice. + - Only meaningful addition is =->=. + +- Tuples! + +- I especially enjoy the section on "Style" in the tutorial. + - snake_case is "acceptable", constants are Initialuppercase. + - 60 characters to line is a big opinion. + +* Building + +** On a Simple Level + +- =mbld -b [binary-name] [source.myr ...]= + +** Build System + +I love this. =[name].proj= looks like + +#+BEGIN_SRC myrddin +lib stack = + stk.myr +;; +#+END_SRC + +then you just run =mbld=. For binaries, replace =lib= with =bin=. + +* Standard Library + +- std.optparse + +* CFFI + +Horribly documented. See [[https://eigenstate.org/archive/myrddin-dev/2015-Oct/0000002.html]]. + +# --- + +- https://github.com/glouw/c8c +- https://github.com/dmatlack/chip8/tree/master/roms +- http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/ diff --git a/org/Gaming Hacking on Linux - scanmem Basics/game-hacking-on-linux-scanmem.org b/org/Gaming Hacking on Linux - scanmem Basics/game-hacking-on-linux-scanmem.org new file mode 100644 index 0000000..6e7cf1f --- /dev/null +++ b/org/Gaming Hacking on Linux - scanmem Basics/game-hacking-on-linux-scanmem.org @@ -0,0 +1,8 @@ +#+TITLE: Game Hacking on Linux - scanmem Basics +#+DATE: <2017-06-18 Sun 11:51> +#+TAGS: tutorial, reverse-engineering, linux, video-games, game-hacking +#+CROSSPOST: https://0x00sec.org/t/game-hacking-on-linux-scanmem-basics/2458 + +Hey, this is a very brief tutorial on scanmem, a memory manipulation tool for +Linux that’s well suited to game hacking. I’ll be using GZDoom as an example, +but you should be able to follow along with any game you want. diff --git a/org/Installing GuixSD: One Month Later/installing-guixsd-one-month-later.org b/org/Installing GuixSD: One Month Later/installing-guixsd-one-month-later.org new file mode 100644 index 0000000..32b6685 --- /dev/null +++ b/org/Installing GuixSD: One Month Later/installing-guixsd-one-month-later.org @@ -0,0 +1,100 @@ +#+TITLE: Installing GuixSD: One Month Later ++DATE: +#+TAGS: opinion, linux, gentoo + +# https://zge.us.to/guix.html + +# https://octodon.social/@cwebber/101816791252611754 +# https://mastodon.sdf.org/@jakob/101857455853919355 +# Stability with v1.0.0 +# https://octodon.social/@cwebber/102089556606069384 + +# It's actually been three months since I began running GuixSD as a daily driver, +# but I wanted to reference a [[/blog/installing-gentoo-one-month-later.html][similar post]] I wrote last year. + +Several months ago, I came across one of Pierre Neidhardt's [[https://ambrevar.xyz/guix-advance/index.html][articles]] regarding +the Guix System Distribution. NixOS was little more than a name to me back then, +so this was my first exposure to the concepts of purely functional package +management. I was pretty stoked about the ideas behind it. + +The article introduces GuixSD as the "fully-programmable OS," explaining that +the init system and package manager are both written in [[https://www.gnu.org/software/guile/][GNU Guile]]. As someone +who often longed for a Lisp interface to [[https://en.wikipedia.org/wiki/Portage_(software)][Portage]], this immediately caught my +attention. Among the other favorite features that Neidhardt lists, I was +particularly enticed by the promises of system roll-backs, support for multiple +simultaneously-installed package versions, a "self-reproducing live USB", and a +GNU Emacs package for working with the package manager. + +#+BEGIN_EXPORT html +<div class="mastodon"> + <iframe height="180" src="https://mastodon.sdf.org/@jakob/101415693778707984/embed"></iframe> +</div> +#+END_EXPORT + +My setup was working just fine, though, and I needed a working computer for +university, so I didn't have much of a compelling reason to distro hop. That is, +until I got word that my GSoC [[https://summerofcode.withgoogle.com/projects/#5232565294727168][proposal]] this year was accepted. I'll say this +much: If a virtual machine is a significant part of your development workflow, +you're gonna have a bad time. + +* Installation + +When I'm not at university, I have two machines that I like to use: a desktop +computer, and a laptop. The desktop computer was my first victim as I'd still be +able to get things done if it were in an unusable state (the laptop was another +story). As it turns out, I picked the harder of the two machines to install +GuixSD to first. The installer would show some messages from Shepherd, and then +continue to boot to a blank screen. I was able to get to a console if I passed +in 'nomodeset' from Grub, so the issue was immediately obvious to me: the +default kernel for GuixSD is [[https://en.wikipedia.org/wiki/Linux-libre][Linux-libre]], an offshoot of Linux that removes any +and all non-free components. Put another way, any firmware blobs that the +tower's graphics card might have need weren't being loaded + +As comforting as the VESA framebuffer console is, I wanted to run an X server. +I'd have to load non-free firmware. Sigh. Please don't blob-shame me. + +This actually isn't too hard to do, and is [[https://ambrevar.xyz/guix-advance/index.html#org1a64edd][covered]] in Pierre's article. What I +did, essentially, was create a Guix configuration with a custom kernel and use +'guix system disk-image' to produce something that I could use on my tower, and +install GuixSD from there. I began with the kernel config I was using for +Gentoo, but that would panic, claiming that Linux couldn't find the root +filesystem. So I copied over GuixSD's config. That seemed to work. This all took +a pretty long time because I was compiling the kernel in a /virtual machine/, and +every time I changed something, Guix would calculate a new derivation for the +kernel, and it would be rebuilt without any of the object files from the +previous compilation. + +When I had a good development environment on the tower, I felt it was time to +install to the laptop. This was far easier as I didn't have to worry about +firmware for the machine to be _usable_. The internal WNIC didn't work, but that +was alright since I had a USB WNIC that didn't require non-free firmware. + +-- + +Being able to configure my machines from a single Scheme source file has +provided me great peace of mind; trying to document the changes I make to the +various configuration files in ~/etc/~ is hopeless. + +There are a number of features that Gentoo brought that I'll miss, but I also +think my missing them is a matter of preference. + +I never really understood slots in Gentoo. I think they allow multiple versions +of a package to be installed at the same time. Regardless, I have no plans to +learn about them now. The use of symbolic links in Guix is a solution to the +multiple-versions problem that's much easier to wrap my head around. + +I always hated the way that Gentoo dealt with configuration files of upgraded +packages. + +"For example, alice installs GCC 4.7.2. As a result, +/home/alice/.guix-profile/bin/gcc points to /gnu/store/…-gcc-4.7.2/bin/gcc. Now, +on the same machine, bob had already installed GCC 4.8.0. The profile of bob +simply continues to point to /gnu/store/…-gcc-4.8.0/bin/gcc—i.e., both versions +of GCC coexist on the same system without any interference." + +- Package "categories" (i.e. app-admin, net-im). This is something that the + BSD's do, and Debian-likes do not. + +The straw that broke the camel's back was this [[https://forum.zdoom.org/viewtopic.php?f=50&t=65552][unusual stuttering]] in anything +running OpenGL. I concluded that this was an issue with SDL or Mesa and +proceeded to nuke everything and reinstall Gentoo. diff --git a/org/Towards Guix for DevOps/towards-guix-for-devops.org b/org/Towards Guix for DevOps/towards-guix-for-devops.org new file mode 100644 index 0000000..afbaea4 --- /dev/null +++ b/org/Towards Guix for DevOps/towards-guix-for-devops.org @@ -0,0 +1,8 @@ +#+TITLE: Towards Guix for DevOps +#+DATE: <2019-07-13 Sat 16:11> +#+TAGS: writeup, programming, functional-programming, linux, guix, lisp, scheme, guile +#+CROSSPOST: https://www.gnu.org/software/guix/blog/2019/towards-guix-for-devops/ + +Hey, there! I'm Jakob, a Google Summer of Code intern and new contributor to +Guix. Since May, I've been working on a DevOps automation tool for the Guix +System, which we've been calling guix deploy. diff --git a/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#2-#11)/challenges-re-writeups-1.org b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#2-#11)/challenges-re-writeups-1.org index 6ea207c..5450a4a 100644 --- a/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#2-#11)/challenges-re-writeups-1.org +++ b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#2-#11)/challenges-re-writeups-1.org @@ -2,12 +2,12 @@ #+DATE: <2019-03-10 Sun> #+TAGS: writeup, reverse-engineering, arm, x86 -As mentioned in the post I wrote [[http://jakob.space/posts/new-ambitions-for-a-new-year/][describing my plans for 2019]], one of my goals -this year is to get through at least 50 of the exercises on Dennis Yurichev's -[[https://challenges.re/][challenges.re]]. I've decided to document my progress in the form of writeups for -the challenges I complete, batched in sets of ten exercises. For each challenge, -I'll try to explain the intuitions that brought me closer to answering the -recurring question from Yurichev, "[w]hat does this code do?" +As mentioned in the (now deleted) post I wrote describing my plans for 2019, one +of my goals this year is to get through at least 50 of the exercises on Dennis +Yurichev's [[https://challenges.re/][challenges.re]]. I've decided to document my progress in the form of +writeups for the challenges I complete, batched in sets of ten exercises. For +each challenge, I'll try to explain the intuitions that brought me closer to +answering the recurring question from Yurichev, "[w]hat does this code do?" * Challenge #2 @@ -30,7 +30,7 @@ listing to AT&T syntax for once. #+END_EXPORT Below is a rough translation of the disassembly listing to C. My process is -relatively unchanged from the workflow I described in an [[http://jakob.space/posts/decompilation-by-hand/][older post]]. +relatively unchanged from the workflow I described in an [[http://jakob.space/blog/decompilation-by-hand.html][older post]]. #+BEGIN_SRC c :hl_lines 0 unsigned f(unsigned a) diff --git a/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#23-#35)/challenges-re-writeups-3.org b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#23-#35)/challenges-re-writeups-3.org new file mode 100644 index 0000000..44b3021 --- /dev/null +++ b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#23-#35)/challenges-re-writeups-3.org @@ -0,0 +1,817 @@ +#+TITLE: Writeups for Dennis Yurichev's Reverse Engineering Challenges (#23-#35) +#+DATE: <2019-08-18 Sun 10:42> +#+TAGS: writeup, reverse-engineering, x86 + +This is the third set of solutions for my self-imposed challenge of completing +at least fifty of the exercises on Dennis Yurichev's [[https://challenges.re][challenges.re]] by the end of +the year. The previous set is available [[http:///jakob.space/challenges-re-writeups-2.html][here]]. + +* Challenge #23 + +The problem is prefaced with, "[t]his is another implementation of a well-known +library function, works only in a 64-bit environment." Translating the +disassembly directly to C reveals unrolled loops, but the intent isn't too +difficult to figure out. + +#+BEGIN_SRC c +int f(char *a) +{ + int i; + + if (a[0] == '\0') { + return 0; + } + + if (a[1] == (char) 0xff) { + return 1; + } + + if (a[2] == (char) 0xff) { + return 2; + } + + if (a[3] == (char) 0xff) { + return 3; + } + + if (a[4] == (char) 0xff) { + return 4; + } + + if (a[5] == (char) 0xff) { + return 5; + } + + i = 0; + + while (a[6] != (char) 0xff) { + if (a[7] == (char) 0xff) { + return i + 7; + } + + i += 8; + a += 8; + + if (a[1] == (char) 0xff) { + return i; + } + + if (a[1] == (char) 0xff) { + return i + 1; + } + + if (a[2] == (char) 0xff) { + return i + 2; + } + + if (a[3] == (char) 0xff) { + return i + 3; + } + + if (a[4] == (char) 0xff) { + return i + 4; + } + + if (a[5] == (char) 0xff) { + return i + 5; + } + } + + return i + 6; +} +#+END_SRC + +=f= returns the index of the first occurrence of =0xff= in =a=. In addition to asking +for the purpose of the code, the challenge poses a few additional questions. + +First: "The code may crash under some specific circumstances. Which are...?" =f= +will crash in the case that there isn't a 0xff character in the string. + +Second: "The code can be easily optimized using SSEx. How?" =movq= can be used to +dereference the characters of =a=, and the location of the =0xff= character can be +found using =pcmpeqb=. Actually implementing this is left as an exercise to the +reader. And I'm not saying that just because writing SIMD by hand makes me want +to break down and cry... or anything like that... + +Finally: "The code will not work correctly on big-endian architectures. How to +fix it?" In the disassembly, the LSB of =rdx= (=dl=) is treated as the _first_ +character in the sliding window. On a big-endian system, dereferencing the +window as an integer would mean that the LSB would correspond with the _last_ +character in the window. To fix this, you would need to change which parts of +the register are being looked at. I realize that's a rather anemic answer, but +the alternative would be going all-in and implementing =f= on a big-endian +platform, which I don't really want to do right now. + +* Challenge #26 + +I decided to skip challenges #24 and #25 as they were listed as "Level 2" and +"Level 3" respectively in terms of difficulty. Challenge #25 in particular +seemed particularly demanding. Challenge #26, on the other hand, was a +relatively straightforward bytecode reverse engineering task. Like Challenge +#14, disassemblies for both .NET and the JVM are given, and as I'm more familiar +with Java than C# (unfortunately), that's the disassembly I chose to work with. + +#+BEGIN_SRC java +public static byte f(byte a) { + return (byte) ((((long) a * 8623620610L) & 1136090292240L) % 1023L); +} +#+END_SRC + +Again, I'm not familiar with JVM bytecode, so I broke out [[https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings][my favorite JVM +reference]]. Here are the instructions we're concerned with: + +#+BEGIN_SRC java +iload_0 // load an int value from local 0 +i2l // convert an int to a long +l2i // convert a long to an int +i2b // convert an int to a byte +ldc2_w // push a constant onto the stack +lmul // multiply two longs +land // perform a bitwise and on two longs +lrem // perform remainder division on two longs +#+END_SRC + +Even if you don't know how the JVM works, I think the purpose of =f= is fairly +clear as soon as you know what those few instructions do. + +I've typically been using Matt Godbolt's amazing [[https://godbolt.org/][Compiler Explorer]] to check my +solutions, but this time around I used [[http://javabytes.io/][Javabytes]]. The disassembly of my +translation for =f= matches what was given for the challenge, so I'm quite +confident in my answer. As for what it does: I began my analysis as I typically +do, giving the function some test values and observing the output. + +#+BEGIN_SRC java +public static void main(String[] args) { + for (int i = 0; i < 256; i++) { + System.out.printf("%3i: b\n", i, f((byte) i)); + } +} + +// 0: 0 +// 1: -128 +// 2: 64 +// 3: -64 +// 4: 32 +// ... +// 253: 63 +// 254: -65 +// 255: 127 +#+END_SRC + +That isn't very telling, but the oscillating sign gives me an idea. + +#+BEGIN_SRC java +public static String toPaddedBinary(byte a) { + String s = String.format("%8s", Integer.toBinaryString(a)); + s = s.replace(' ', '0'); + return s.substring(s.length() - 8, s.length()); +} + +public static void main(String[] args) { + for (int i = 0; i < 256; i++) { + System.out.printf("%s: %s\n", toPaddedBinary((byte) i), toPaddedBinary(f((byte) i))); + } +} + +// 00000000: 00000000 +// 00000001: 10000000 +// 00000010: 01000000 +// ... +// 11111101: 10111111 +// 11111110: 01111111 +// 11111111: 11111111 +#+END_SRC + +So =f= reverses the bits of =a=. + +* Challenge #27 + +This challenge threw me for a bit of a loop, as it didn't give the usual amd64 +output from GCC 4.9. Rather an i386 disassembly from MSVC 2010 was given, +alongside an arm64 disassembly from GCC 4.9. I tried both, but had some +significant trouble with the MSVC disassembly as it seemed to be dealing with +64-bit integers on a 32-bit architecture. + +After quickly reviewing CDOT's [[https://wiki.cdot.senecacollege.ca/wiki/Aarch64_Register_and_Instruction_Quick_Start#General-Purpose_Registers][AArch64 reference]] to get an idea of register +widths, this is the translation came up with: + +#+BEGIN_SRC c +int f(int a) +{ + return (((int) (((long) a * 0xc64b2279) >> 32)) + a) >> 9 - (a >> 31); +} +#+END_SRC + +I'm not particularly confident in this, however, as the behavior of =f= is to +return \(floor(a / 289)\). I suspect my poor understanding of the [[http://infocenter.arm.com/help/topic/com.arm.doc.dui0068b/CIHBEAGE.html][flexible +second operand]] (i.e. in =sub w0, w1, w0, asr 31=) is what gave me me the most +trouble. Perhaps this is a challenge I should return to when I properly learn +ARM. + +I tried a more direct translation to Python, + +#+BEGIN_SRC python +def test_f(a): + result = a * 0xc64b2279 + upper = result & 0xffffffff00000000 + lower = result & 0xffffffff + return ((upper + lower) >> 9) + \ + (((upper + lower) >> 9) >> 31) + +fmt = lambda n: bin(n)[2:].rjust(32, '0') + +for i in range(256): + print("{}\n{}\n".format(fmt(i), fmt(test_f(i)))) +#+END_SRC + +which didn't yield any recognizable patterns. + +Actually, before implementing it in Python, I implemented it in Emacs Lisp (I +might have been waiting on Python to compile? I don't remember). + +#+BEGIN_SRC elisp +(defun test-f (a) + (let* ((result (* a #xc64b2279)) + (upper (logand result #xffffffff00000000)) + (lower (logand result #xffffffff))) + (+ (ash (+ upper lower) 9) + (ash (ash (+ upper lower) 9) 31)))) +#+END_SRC + +Either way, this challenge wasn't fruitful. + +* Challenge #28 + +I suspect that this challenge was made a bit easier by GCC's optimizations. The +amd64 disassembly includes two unused functions, =f2= and =my_memdup= -- they're +used in some of the other disassemblies, but I chose to ignore them. + +#+BEGIN_SRC c +#include <string.h> +#include <stdlib.h> + +int f1(int *a, int *b) +{ + return *a > *b ? 0 : -1; +} + +int f_main(void *src, int n) +{ + int tmp; + char *dst; + + dst = malloc(n * sizeof(int)); + memcpy(dst, src, n * sizeof(int)); + qsort(dst, n, sizeof(int), f1); + + if (n > 1) { + tmp = dst[n >> 1] + \ + dst[n >> 1 - 1]; + return (tmp + (tmp >> 31)) >> 1; + } + + return dst[0]; +} +#+END_SRC + +I've started to see this =(tmp + (tmp >> 31)) >> 1= idiom rather frequently, so I +decided to finally look it up, coming across [[https://stackoverflow.com/questions/40638335/why-does-the-compiler-generate-a-right-shift-by-31-bits-when-dividing-by-2][this]] Stack Overflow answer. I'm +glad I did, because realizing that it carries out signed integer division by two +makes this exercise far more clear. + +#+BEGIN_SRC c +#include <string.h> +#include <stdlib.h> + +int f1(int *a, int *b) +{ + return *a > *b ? 0 : -1; +} + +int f_main(void *src, int n) +{ + char *dst; + + dst = malloc(n * sizeof(int)); + memcpy(dst, src, n * sizeof(int)); + qsort(dst, n, sizeof(int), f1); + + if (n > 1) { + return (dst[n / 2] + dst[n / 2 - 1]) / 2; + } + + return dst[0]; +} +#+END_SRC + +=f_main= returns the [[https://en.wikipedia.org/wiki/Median][median]] of a set of values. + +* Challenge #30 + +I have, once again, skipped another challenge that was being listed as "level +2," which brings us to the thirtieth challenge. This one is strikingly different +from the other challenges I've covered here; rather than being asked to describe +what a program does, the instruction read: + +"This program requires a password. Try to find it. + +As an additional exercise, try to change the password by patching the executable +file. Also try using one with a different length. What is the shortest possible +password here? + +Also try to crash the program using only string input." + +We're given several links to downloads. Binaries are provided for 32-bit +Microsoft Windows, Mac OS X, and i386/mips Linux. I went with i386 Linux, as I'd +be able to run the challenge natively.q + +#+BEGIN_SRC +jakob@Epsilon /tmp $ sha256sum password1 +96b8110208d61c7ac586910ebad22ef2e4bbeb867e6d6429967846698b9d02fc password1 +#+END_SRC + +Being faced with a binary, I was tempted to use this as an opportunity to try +out [[https://ghidra-sre.org/][Ghidra]], but while I waited for OpenJDK 11 to download, I peered inside with +radare2 and decided that it wasn't worth the trouble. Here's the disassembly, +according to radare: + +#+BEGIN_SRC +[0x080484ed]> pdf + ;-- eip: +┌ (fcn) main 149 +│ main (); +│ ; var int local_4h @ esp+0x4 +│ ; var int local_1ch @ esp+0x1c +│ ; var int local_9ch @ esp+0x9c +│ ; DATA XREF from 0x08048407 (entry0) +│ 0x080484ed 55 pushl %ebp +│ 0x080484ee 89e5 movl %esp, %ebp +│ 0x080484f0 83e4f0 andl $0xfffffff0, %esp +│ 0x080484f3 81eca0000000 subl $0xa0, %esp +│ 0x080484f9 65a114000000 movl %gs:0x14, %eax ; [0x14:4]=-1 ; 20 +│ 0x080484ff 8984249c0000. movl %eax, local_9ch +│ 0x08048506 31c0 xorl %eax, %eax +│ 0x08048508 c70424208604. movl $str.enter_password:, 0(%esp) ; [0x8048620:4]=0x65746e65 ; "enter password:" +│ 0x0804850f e89cfeffff calll sym.imp.puts ; int puts(const char *s) +│ 0x08048514 8d44241c leal local_1ch, %eax ; 0x1c ; 28 +│ 0x08048518 89442404 movl %eax, local_4h +│ 0x0804851c c70424308604. movl $0x8048630, 0(%esp) ; [0x8048630:4]=0x6e007325 +│ 0x08048523 e8b8feffff calll sym.imp.__isoc99_scanf +│ 0x08048528 83f801 cmpl $1, %eax ; 1 +│ ┌─< 0x0804852b 740c je 0x8048539 +│ │ 0x0804852d c70424338604. movl $str.no_password_supplied, 0(%esp) ; [0x8048633:4]=0x70206f6e ; "no password supplied" +│ │ 0x08048534 e877feffff calll sym.imp.puts ; int puts(const char *s) +│ │ ; JMP XREF from 0x0804852b (main) +│ └─> 0x08048539 c74424044886. movl $str.metallica, local_4h ; [0x8048648:4]=0x6174656d ; "metallica" +│ 0x08048541 8d44241c leal local_1ch, %eax ; 0x1c ; 28 +│ 0x08048545 890424 movl %eax, 0(%esp) +│ 0x08048548 e843feffff calll sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) +│ 0x0804854d 85c0 testl %eax, %eax +│ ┌─< 0x0804854f 750e jne 0x804855f +│ │ 0x08048551 c70424528604. movl $str.password_is_correct, 0(%esp) ; [0x8048652:4]=0x73736170 ; "password is correct" +│ │ 0x08048558 e853feffff calll sym.imp.puts ; int puts(const char *s) +│ ┌──< 0x0804855d eb0c jmp 0x804856b +│ ││ ; JMP XREF from 0x0804854f (main) +│ │└─> 0x0804855f c70424668604. movl $str.password_is_not_correct, 0(%esp) ; [0x8048666:4]=0x73736170 ; "password is not correct" +│ │ 0x08048566 e845feffff calll sym.imp.puts ; int puts(const char *s) +│ │ ; JMP XREF from 0x0804855d (main) +│ └──> 0x0804856b 8b94249c0000. movl local_9ch, %edx ; [0x9c:4]=-1 ; 156 +│ 0x08048572 653315140000. xorl %gs:0x14, %edx +│ ┌─< 0x08048579 7405 je 0x8048580 +│ │ 0x0804857b e820feffff calll sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) +│ │ ; JMP XREF from 0x08048579 (main) +│ └─> 0x08048580 c9 leave +└ 0x08048581 c3 retl +#+END_SRC + +As you can see, this is just like any other "easy crackme." A simple string +comparison. radare2 doesn't automatically decode 0x8048630 as a string, but it's +trivial to obtain its value. + +#+BEGIN_SRC +:> psz @ 0x8048630 +%s +#+END_SRC + +Translating it into C is similarly trivial. + +#+BEGIN_SRC c +#include <stdio.h> +#include <string.h> + +int main(void) +{ + char buf[128]; + puts("enter password:"); + if (scanf("%s", buf) != 1) { + puts("no password supplied"); + } + if (strcmp(buf, "metallica") == 0) { + puts("password is correct"); + } else { + puts("password is not correct"); + } +} +#+END_SRC + +I do have to complement Yurichev's choice of strong passwords. \m/ + +One may wonder where I pulled =128= from. Our stack layout looks something like +this: + +#+BEGIN_SRC prog ++-------------------------------------+ +|%esp | +|Scratch space for function arguments.| ++-------------------------------------+ +|%esp + 0x1c | +|Buffer starts here | +|... | +|Buffer ends here | ++-------------------------------------+ +|%esp + 0x9c | +|Stack canary, perhaps? | ++-------------------------------------+ +|%esp + I DON'T CARE ANYMORE | +|Here be dragons. | ++-------------------------------------+ +#+END_SRC + +radare2 is kind enough to automatically name local variables according to their +position in the stack layout, so I was able to derive this from the names +=local_1ch= and =local_9ch=. =local_4h= isn't really a local variable -- it looks like +one, but that's just how the compiler decided to set up arguments for the +various function calls (dereferencing the stack pointer, as opposed to using +=push=). Anyway, subtracting =0x9c= from =0x1c= gets you 128 -- hence, the buffer size +in my translation. + +It's pretty easy to patch the password, since =strcmp= operates on C strings. Just +patch the characters. No sort of length needs to be adjusted since they're +null-terminated. The shortest possible password would be zero characters long, +which would be achieved by patching in a null byte at the 'm' in "metallica". +This can be done however you like, though radare makes it easy if you've opened +the file in "write mode": just seek to the location of the 'm' and =wx 00=. +Crashing the program is similarly easy, since there are no bounds checks on the +call to =scanf=. + +#+BEGIN_SRC +jakob@Epsilon /tmp $ python -c "print('a' * 256)" | ./test +enter password: +password is not correct +Segmentation fault +#+END_SRC + +* Challenge #31 + +Yowch. We're only given disassemblies from MSVC this time. + +#+BEGIN_SRC c +double f(double a, int b) +{ + double cur; + cur = 1.0; + while (((double) (((int) (cur - a)) - b)) <= 0.001) + cur = (a + 1.0) * 0.5; + return cur; +} +#+END_SRC + +Once again, I deferred to [[https://float.exposed/][float.exposed]] to decode the floating-point constant +values. =__real@3ff0000000000000= is =1.0=, =__real@3f50624dd2f1a9fc= is approximately +=0.001=, and =__real@3fe0000000000000= is =0.5=. I also needed to look up most of the +SIMD instructions. =cvttsd2si= converts a double to an int, =cdq= converts an int +into a long, =cvtdq2pd= converts an int to a double, and =comisd= is comparable to +=cmp=. + +This converges for very few values. Which is a pain, since this translation +gives me some very promising output in [[https://godbolt.org/][Compiler Explorer]]. But considering the +value that the loop gets stuck on, I suspect that =f= averages =a= and =b=. + +* Challenge #32 + +We're given a hint that, "[t]his is a standard C library function. The source +code is taken from MSVC 2010." + +#+BEGIN_SRC c +#include <stddef.h> + +char *f(char *a, char *b) +{ + char *cur; + char *a_cur; + char *b_cur; + + cur = a; + + if (*b == '\0') { + return a; + } + + while (*cur != '\0') { + a_cur = cur; + b_cur = b; + + while (*a_cur != '\0' && *b_cur != '\0' && *a_cur == *b_cur) { + a_cur++; + b_cur++; + } + + if (*b_cur == '\0') { + return cur; + } + + cur++; + } + + return NULL; +} +#+END_SRC + +I think the translation makes the purpose of this function reasonably clear, but +the hint means I can verify my work against C's tiny standard library. =f= is +obviously one of the library's [[https://en.wikipedia.org/wiki/C_string_handling][string functions]]. Can you guess which one? + +(My answer is that =f= is an implementation of =strstr=.) + +* Challenge #33 + +What gave it away for me this time was the "crypto" tag. I stopped in my +translation efforts about here, + +#+BEGIN_SRC c +void f(void *a, void *b, void *c) +{ + int mushroom; // _k0 + int bean; // _k1 + int tomato; // _k2 + int corn; // _k3 + + // eax = a[0] + // ecx = a[1] + + mushroom = b[0]; + bean = b[1]; + + // esi = b[3]; + // edx = 0; + + tomato = b[2]; + corn = b[3]; + + // edi = 32; + + // LL8 + esi = ecx >> 5 + bean; + ebx = ecx << 4 + mushroom; + edx -= 0x61c88647; + + esi ^= ebx; + ebx = ecx + edx; + esi ^= ebx; + + eax += esi; + + esi = eax >> 5 + corn; + ebx = eax << 4 + tomato; + + esi ^= ebx; + ebx = eax + edx; + esi ^= ebx; + + ecx += esi; + edi--; + + // When edi == 0: c[0] = eax, c[1] = ecx +} +#+END_SRC + +and decided to do a search for '0x61c88647 hash'. This yields a few interesting +results, such as [[https://stackoverflow.com/questions/38994306/what-is-the-meaning-of-0x61c88647-constant-in-threadlocal-java][one]] describing the constant used in ThreadLocal.java's +implementation Fibonacci hashing and [[https://softwareengineering.stackexchange.com/questions/63595/tea-algorithm-constant-0x9e3779b9-said-to-be-derived-from-golden-ratio-but-the][another]] describing the constants used in +the Tiny Encryption Algorithm. + +This immediately set off bells for me. I read Bruce Schneier's /Applied +Cryptography/ some years back and was instantly reminded that TEA uses [[https://en.wikipedia.org/wiki/Block_cipher#Operations][ARX]] with +shifts of =5= and =4=. If you pull up Wikipedia's reference code for TEA encryption, +you'll be greeted with the following: + +#+BEGIN_SRC c +void encrypt (uint32_t v[2], uint32_t k[4]) { + uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */ + uint32_t delta=0x9E3779B9; /* a key schedule constant */ + uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ + for (i=0; i<32; i++) { /* basic cycle start */ + sum += delta; + v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1); + v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); + } /* end cycle */ + v[0]=v0; v[1]=v1; +} +#+END_SRC + +Armed with this, I can confidently say that =f= is an implementation of TEA +encryption with a schedule constant of =0x61c88647=. + +* Challenge #34 + +Another crypto challenge. This time, we're told that "[t]his is a well-known +cryptographic algorithm from the past." The disassembly was simple enough that I +thought to translate it into standard mathematical notation rather than C, but +it turned out to be far less helpful than the equivalent C. + +#+BEGIN_SRC c +uint16_t f(uint16_t a) +{ + uint16_t tmp; + + tmp = a << 2; + tmp ^= a; + tmp <<= 1; + tmp ^= a; + tmp <<= 2; + tmp ^= a; + + return ((tmp & 32) << 10) | (a >> 1); +} +#+END_SRC + +That said, I'm not familiar with the particular algorithm. There's a clear +pattern, but I'm not sure where to start looking. Is it a hash function? Some +kind of bastardized XOR encryption? Who knows. + +* Challenge #35 + +This was a tough one. I'll give my initial translation to C and explain where I +went wrong: + +#+BEGIN_SRC c +#include <strings.h> +#include <stdio.h> + +int f(int x, int y) +{ + int a, b; + + if (x == 0) { + return y; + } + + if (y == 0) { + return x; + } + + a = x >> ffs(x); + b = y >> ffs(y); + + while (a != b) { + if (a < b) { + SWAP(a, b); + } + + if (a == 1) { + break; + } + + b = (b - a) >> ffs(b - a); + } + + return a << ffs(x | y); +} +#+END_SRC + +One thing worth remarking on in the disassembly is this: + +#+BEGIN_SRC asm + xor esi, edx + xor edx, esi + xor esi, edx +#+END_SRC + +This is the [[https://en.wikipedia.org/wiki/XOR_swap_algorithm][XOR swap algorithm]]. In an attempt to make the translation more +clear, I replaced it with a (non-existent) =SWAP= macro. =ffs= is also a POSIX +extension that [[https://stackoverflow.com/questions/757059/position-of-least-significant-bit-that-is-set][corresponds nicely]] to the =bsf= instruction. + +The issue? I've been reading these MSVC disassemblies wrong the whole time. Take +this instruction, for example: =mov edx, DWORD PTR _y$[esp+4]=. I'd never actually +done out the calculations. As it turns out, =_rt$2[esp+8]= aliases with =y=. I +thought that =_rt$2= was a distinct variable and that the compiler was storing to +some local variable but never using it. This isn't the case, hence why the +translation doesn't work as intended. + +What I need to start doing for these MSVC disassemblies is translating them into +something I can assemble. + +#+BEGIN_SRC asm +global f +f: + push ecx + push esi + mov esi, DWORD [esp+12] + test esi, esi + jne init + mov eax, DWORD [esp+16] + pop esi + pop ecx + ret +init: + mov edx, DWORD [esp+16] + mov eax, esi + test edx, edx + je exit + or eax, edx + push edi + bsf edi, eax + bsf eax, esi + mov ecx, eax + mov DWORD [esp+8], eax + bsf eax, edx + shr esi, cl + mov ecx, eax + shr edx, cl + mov DWORD [esp+16], eax + cmp esi, edx + je return +lp: + jbe skip + xor esi, edx + xor edx, esi + xor esi, edx +skip: + cmp esi, 1 + je return + sub edx, esi + bsf eax, edx + mov ecx, eax + shr edx, cl + mov DWORD [esp+16], eax + cmp esi, edx + jne lp +return: + mov ecx, edi + shl esi, cl + pop edi + mov eax, esi +exit: + pop esi + pop ecx + ret 0 +#+END_SRC + +Actually, I should be doing this for all of the challenges... Anyway, observing +a few test values for =f=: + +#+BEGIN_SRC prog +f(1, 1) = 1 +f(1, 2) = 1 +f(1, 3) = 1 +f(1, 4) = 1 +f(1, 5) = 1 +f(1, 6) = 1 +f(1, 7) = 1 +f(1, 8) = 1 +f(1, 9) = 1 +... +f(1, 252) = 1 +f(1, 253) = 1 +f(1, 254) = 1 +f(1, 255) = 1 +f(2, 1) = 1 +f(2, 2) = 2 +f(2, 3) = 1 +f(2, 4) = 2 +f(2, 5) = 1 +f(2, 6) = 2 +f(2, 7) = 1 +f(2, 8) = 2 +f(2, 9) = 1 +f(2, 10) = 2 +f(2, 11) = 1 +f(2, 12) = 2 +... +f(9, 1) = 1 +f(9, 2) = 1 +f(9, 3) = 3 +f(9, 4) = 1 +f(9, 5) = 1 +f(9, 6) = 3 +f(9, 7) = 1 +f(9, 8) = 1 +f(9, 9) = 9 +... +f(10, 1) = 1 +f(10, 2) = 2 +f(10, 3) = 1 +f(10, 4) = 2 +f(10, 5) = 5 +f(10, 6) = 2 +f(10, 7) = 1 +f(10, 8) = 2 +f(10, 9) = 1 +f(10, 10) = 10 +f(10, 11) = 1 +f(10, 12) = 2 +... +#+END_SRC + +It took me a while, but I eventually noticed the pattern. =f= is the [[https://en.wikipedia.org/wiki/Greatest_common_divisor][greatest +common divisor]] function. diff --git a/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#36-)/challenges-re-writeups-4.org b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#36-)/challenges-re-writeups-4.org new file mode 100644 index 0000000..a3a1990 --- /dev/null +++ b/org/Writeups for Dennis Yurichev's Reverse Engineering Challenges (#36-)/challenges-re-writeups-4.org @@ -0,0 +1,996 @@ +#+TITLE: Writeups for Dennis Yurichev's Reverse Engineering Challenges (#36-) +#+TAGS: writeup, reverse-engineering, x86 + +* Challenge #36 + +The description this time describes that this is "[a] well-known algorithm +again. What does it do? Also, take notice that the code for x86 uses FPU, but +SIMD instructions are used instead in the x64 code. That's OK." + +#+BEGIN_SRC c +long state = 0x12345678; + +float f1(void) +{ + state = state * 0x19660d + 0x3c6ef35f; + return ((float) ((state & 0x7fffff) | 0x40000000)) - 3.0f; +} + +void f(void) +{ + int i; + int count; + float a; + float b; + + for (i = 0, count = 0; i < 1000000; i++) { + a = f1(); + b = f1(); + + if (a * a + b * b > 1.0f) { + count++ + } + } + + ((float) (((double) count) * 2.25) / 10.9073486328125); +} +#+END_SRC + +I thought this was a lame challenge. The floating point operations of =f1= have +been optimized to the point that it's unrecognizable, so if you aren't familiar +with the standard bit-twiddling tricks that GCC uses to speed up floating-point +operations, you aren't going to be able to come up with anything meaningful -- +especially since neither function take parameters. My response? =f= returns the +constant value =206282.937500=. + +I thought this might be the [[https://en.wikipedia.org/wiki/Fast_inverse_square_root][fast inverse square root]], but I don't believe it is. + +* Challenge #37 + +Ah, another challenge for which the description is that it is a "[w]ell-known +function" and only x86 disassembly given is from MSVC. Fortunately, this one is +not too difficult. + +#+BEGIN_SRC c +int f(int a, int b) +{ + int i; + int n; + + if (a == 0) { + return b + 1; + } + + n = b; + i = a; + + do { + if (n == 0) { + n = 1; + } else { + n = f(i, n - 1); + } + } while (--i != 0); + + return n + 1; +} +#+END_SRC + +This is the Ackermann function, albeit using a loop rather than a direct +translation of the Ackermann–Péter function to code. + +To answer Yurichev's additional questions, a stack overflow occurs if 4 and 2 +are supplied as input because those are [[https://www.wolframalpha.com/input/?i=Ackermann(4,2)][absurd parameters for this function]], and +this function bears the error of not enforcing the constraints given in the +definition of the Ackermann–Péter function. + +* Challenge #38 + +Ah, fun. Another challenge provided as a binary. + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ sha256sum 17 +8f73f329e0988968a9fa40f61da906e83b46817bcb5c0e93f7e95aa74c30e8e0 17 +jakob@Upsilon ~ $ file 17 +17: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=bdeac54f2d98db38d7a63a43f1c41857432686c4, stripped +#+END_SRC + +I stopped a bit early because, for once, the question wasn't "[w]hat does this +code do?", but was instead, "[t]his program prints some information to stdout, +each time different. What is that?" + +#+BEGIN_SRC c +#include <stdlib.h> +#include <time.h> + +static time_t current_time; + +int fcn.004006c4(void) +{ + return current_time = current_time * 0x19660d * 0x3c6ef35f; +} + +int main(int argc, char **argv) +{ + char *s2; + void **s1; + int var_1ch; + int var_18h; + int var_11h; + + s2 = malloc(230); + s1 = malloc(80); + var_1ch = 0; + + while (var_1ch <= 9) { + s1[var_1ch++] = calloc(230, 1); + } + + current_time = time(NULL); + + var_1ch = 0; + while (var_1ch <= 230) { + var_11h = fcn.00400b60(fcn.004006c4()); + s2[var_1ch++] = var_11h & 1; + } + + var_1ch = 0; + while (1) { + fcn.00400970(s2, 230); + fcn.0040072a(s2, 230, 110); + + var_18h = 0; + while (var_18h <= 8) { + if (!memcmp(s1[var_18h++], s2, 230)) { + exit(0); + } + } + + var_18h = 0; + while (var_18h <= 8) { + memcpy(s1[var_18h], s1[++var_18h], 230); + } + + memcpy(s1[9], s2, 230); + var_1ch++; + } +} +#+END_SRC + +The only nondeterminism I saw in the disassembly was from =time=. The actual +output of the program is incomprehensible -- appearing to be some sort of +ASCII-art fractal. For this reason, I'm concluding that the information printed +to =stdout= is the current time. + +* Challenge #39 + +The description for this challenge had me quite excited. "This program requires +a password. Find it." Yes, I love crackmes! + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ sha256sum password2 +8c8365f316de896c453511c5f484755600208b87ad0f1595a2900cbf5a36db24 password2 +#+END_SRC + +=main= is simple enough that I feel I can omit the: it reads in a password with +=scanf=, and then checks it with the following snippet. + +#+BEGIN_SRC prog +│ 0x0804853e e87affffff calll fcn.080484bd +│ 0x08048543 3df8010000 cmpl $0x1f8, %eax ; 504 +#+END_SRC + +We want to find some =password= such that =fcn.080484bd(password) = 0x1f8=. +Peeking into =fcn.080484bd=, I was a little disappointed. + +#+BEGIN_SRC prog +┌ (fcn) fcn.080484bd 46 +│ fcn.080484bd (int32_t arg_8h); +│ ; var int32_t var_4h @ ebp-0x4 +│ ; arg int32_t arg_8h @ ebp+0x8 +│ ; CALL XREF from main @ 0x804853e +│ 0x080484bd 55 pushl %ebp +│ 0x080484be 89e5 movl %esp, %ebp +│ 0x080484c0 83ec10 subl $0x10, %esp +│ 0x080484c3 c745fc000000. movl $0, var_4h +│ ┌─< 0x080484ca eb10 jmp 0x80484dc +│ │ ; CODE XREF from fcn.080484bd @ 0x80484e4 +│ ┌──> 0x080484cc 8b4508 movl arg_8h, %eax ; [0x8:4]=-1 ; 8 ; edx +│ ╎│ 0x080484cf 0fb600 movzbl 0(%eax), %eax +│ ╎│ 0x080484d2 0fbec0 movsbl %al, %eax +│ ╎│ 0x080484d5 0145fc addl %eax, var_4h +│ ╎│ 0x080484d8 83450801 addl $1, arg_8h ; [0x8:4]=-1 ; 1 +│ ╎│ ; CODE XREF from fcn.080484bd @ 0x80484ca +│ ╎└─> 0x080484dc 8b4508 movl arg_8h, %eax ; [0x8:4]=-1 ; 8 ; edx +│ ╎ 0x080484df 0fb600 movzbl 0(%eax), %eax +│ ╎ 0x080484e2 84c0 testb %al, %al +│ └──< 0x080484e4 75e6 jne 0x80484cc ; likely +│ 0x080484e6 8b45fc movl var_4h, %eax ; edx +│ 0x080484e9 c9 leave ; edx +└ 0x080484ea c3 retl ; edx +#+END_SRC + +Do I even need to provide a C translation? The disassembly should be glaringly +obvious: this "check" function just returns the sum of the string argument's +individual bytes. Coming up with a valid password is trivial. + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ ./password2 +enter password: +AAAAAAA1 +password is correct +#+END_SRC + +The problem also suggests that I "try to change the password by patching the +executable file," but this doesn't invokve anything more than changing the word +at =0x08048544=. + +* Challenge #41 + +The question this time is: "[t]his program prints some numbers to stdout. What +is it?" + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ file problem +problem: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=a89ecf1ae2f2474548d09ca3ebccd7db4162fa1e, stripped +jakob@Upsilon ~ $ sha256sum problem +ab3864e8fceeffe4b166cb7481332e88a1fe90b6a406e635c6921119c91a00fd problem +#+END_SRC + +I wrote a C translation without running the program. In retrospect, this wasn't +a bad idea. The calculation is a function of some integer, but the binary spits +out subsequent numbers with no sort of delimitation. Having the C translation +means that I could add a =printf("\n");= after the call to =fcn_00400536(var_4h++);= +and get output similar to the following: + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ /tmp/test +1 + +2 +1 + +3 +10 +5 +16 +8 +4 +2 +1 + +4 +2 +1 + +5 +16 +8 +4 +2 +1 +#+END_SRC + +Here's the C translation. + +#+BEGIN_SRC c +void fcn_00400536(int a) +{ + printf("%d\n", a); + while (a != 1) { + if (a & 1 != 0) { + a = a * 3 + 1; + } else { + a >>= 1; + } + printf("%d\n", a); + } +} + +int main(int argc, char **argv) +{ + int var_4h; + var_4h = 1; + while (var_4h <= 15) { + fcn_00400536(var_4h++); + } + return var_4h; +} +#+END_SRC + +We can pick any interesting sequence and plug it into [[https://oeis.org/search?q=3%2C10%2C5%2C16%2C8%2C4%2C2%2C1&language=english&go=Search][OEIS]], which identifies +=fcn_00400536= as "A070165: Irregular triangle read by rows giving trajectory of n +in Collatz problem." Ah, yes. This is looking familiar now. This is the famously +unsolved problem in mathematics, the [[https://en.wikipedia.org/wiki/Collatz_conjecture][Collatz conjecture]]. + +* Challenge #43 + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ file unknown_utility_2_3 +unknown_utility_2_3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cb74037dd37694879f6250bfb5623c273ef68ca6, stripped +jakob@Upsilon ~ $ sha256sum unknown_utility_2_3 +9d3df3be78f21971059ba2d9973a1358865ccbe2f47f78fc5800779d6f6083fe unknown_utility_2_3 +#+END_SRC + +I really wasn't feeling it today, so I took the easy way out and just ran the +binary provided on a test file. It spits out a floating point number, which +seems to decrease as the file becomes less interesting. Just a hunch, but maybe +it's binary entropy? + +#+BEGIN_SRC prog +jakob@Upsilon ~ $ python -c "print('a' * 256)" > test.txt +jakob@Upsilon ~ $ ./unknown_utility_2_3 test.txt +0.036753 +jakob@Upsilon ~ $ rahash2 -a entropy test.txt +test.txt: 0x00000000-0x00000100 entropy: 0.03675295 +jakob@Upsilon ~ $ dd bs=256 count=1 if=/dev/urandom > test.txt +1+0 records in +1+0 records out +256 bytes copied, 7.0438e-05 s, 3.6 MB/s +jakob@Upsilon ~ $ ./unknown_utility_2_3 test.txt +7.069718 +jakob@Upsilon ~ $ rahash2 -a entropy test.txt +test.txt: 0x00000000-0x000000ff entropy: 7.06971784 +#+END_SRC + +Well, that's an answer I'm certainly happy with. + +* Challenge #48 + +It looks like we're starting to get into the realm of =win32=. The question for +this challenge is, "[w]hat does this win32-function do?" + +#+BEGIN_SRC asm +main: + push 0xFFFFFFFF + call MessageBeep + xor eax,eax + retn +#+END_SRC + +This is pretty simple. It's a wrapper for =MessageBeep=. According to [[https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebeep][MSDN]], the +=0xFFFFFFFF= parameter produces "[a] simple beep. If the sound card is not +available, the sound is generated using the speaker." + +* Challenge #49 + +Another rather simple one. The disassembly for this challenge is given in AT&T +syntax, which is my preferred way of reading x86 assembly. + +#+BEGIN_SRC asm +main: + pushq %rbp + movq %rsp, %rbp + movl $2, %edi + call sleep + popq %rbp + ret +#+END_SRC + +A wrapper around =sleep=, presumably the only provided by =unistd.h=, calling it +with an argument of two seconds. + +* Challenge #52 + +Another simple disassembly: + +#+BEGIN_SRC asm +$SG3103 DB '%d', 0aH, 00H + +_main PROC + push 0 + call DWORD PTR __imp___time64 + push edx + push eax + push OFFSET $SG3103 ; '%d' + call DWORD PTR __imp__printf + add esp, 16 + xor eax, eax + ret 0 +_main ENDP +#+END_SRC + +To copy straight from [[https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/time-time32-time64?view=vs-2019][MSDN]], this prints the number of "seconds elapsed since +midnight (00:00:00), January 1, 1970, Coordinated Universal Time (UTC)." + +MSDN also answers the follow-up question: "=time= is a wrapper for =_time64= and +=time_t= is, by default, equivalent to =__time64_t=. If you need to force the +compiler to interpret =time_t= as the old 32-bit =time_t=, you can define +=_USE_32BIT_TIME_T=. This is not recommended because your application may fail +after January 18, 2038; the use of this macro is not allowed on 64-bit +platforms." + +* Challenge #53 + +I thought this was an interesting challenge. "This code, compiled in Linux +x86-64 using GCC is crashing while execution (segmentation fault). It's also +crashed if compiled by MinGW for win32. However, it works in Windows environment +if compiled by MSVC 2010 x86. Why?" + +#+BEGIN_SRC c +#include <string.h> +#include <stdio.h> + +void alter_string(char *s) +{ + strcpy (s, "Goodbye!"); + printf ("Result: %s\n", s); +}; + +int main() +{ + alter_string ("Hello, world!\n"); +}; +#+END_SRC + +The code is modifying a string constant, which GCC tends to put in a read-only +memory segment (=.rodata=) in the resultant executable. Writing to a read-only +memory segment will cause a segmentation fault. I haven't tested it, but the +question statement makes me think that MSVC puts string constants in a writable +segment, so this would work just fine. + +* Challenge #54 + +No disassembly is given for this challenge. The only thing on the page is "[w]hy +isn't the x86 LOOP instruction used by modern compilers anymore?" Some searching +yields [[https://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have-implemented-it-efficiently/35743699#35743699][this Stack Overflow answer]]. Basically, =loop= is from the time before x86 +became horribly complex, and so on modern processors, it's slow. + +* Challenge #56 + +I decided to skip challenge #55, as it would really just be a walkthrough of +which r2 commands I used. Challenge #56 is not particularly difficult. I went +along with the disassembly from MSVC. + +#+BEGIN_SRC c +#include <stdio.h> + +int main(void) +{ + int n; + n = 100; + do { + printf("%d", n); + } while (n-- != 0); + return 0; +} +#+END_SRC + +The code prints the integers from 100 to 0. + +* Challenge #57 + +This is almost the same disassembly as last time. + +#+BEGIN_SRC c +#include <stdio.h> + +int main(void) +{ + int n; + n = 1; + do { + printf("%d", n); + n += 3; + } while (n < 100); + return 0; +} +#+END_SRC + +* Challenge #58 + +This time, we're fortunate enough to be given a disassembly from GCC 4.8.1, +albeit with =-O3=. + +#+BEGIN_SRC c +int f(char *a) +{ + int count; + count = 0; + while (*a != '\0') { + if (*a++ == ' ') { + count++; + } + } + return count; +} +#+END_SRC + +=f= counts the number of spaces in a given string. As an aside, when I was first +learning to read assembly, I recall someone describing =-O3= as "unintelligible to +humans." The more reverse engineering I've done, the more I've realized that the +optimizations at that level tend to not be as absurd as people make them out to +be. I considered this to be an easy challenge. + +* Challenge #59 + +This one was /really/ easy. + +#+BEGIN_SRC asm +_a$ = 8 +_f PROC + mov ecx, DWORD PTR _a$[esp-4] + lea eax, DWORD PTR [ecx*8] + sub eax, ecx + ret 0 +_f ENDP +#+END_SRC + +The function just returns =a * 7=. I suspect the multiplication followed by +subtraction was an optimization, since multiplication by a power of two can be +implemented as a left shift. + +* Challenge #61 + +Perhaps the most difficult part of this challenge was going out my way to [[https://float.exposed/0x4014000000000000][ensure +that the constant really was 5.0]]. + +#+BEGIN_SRC c +double f(double a, double b, double c, double d, double e) +{ + return (a + b + c + d + e) / 5; +} +#+END_SRC + +=f= simply averages five numbers. + +* Challenge #62 + +The challenge notes that the compiler was optimizing for space, which may +explain the pointless nested loop. + +#+BEGIN_SRC c +void f(float *a, float *b, float *c) +{ + int i; + int j; + + long coffee; + long cake; + + coffee = a - b; + cake = c - b; + + for (i = 200; i > 0; i--) { + for (j = 100; j > 0; j--) { + b[cake] = b[0] + b[coffee]; + b += 8; + } + } +} +#+END_SRC + +=f= adds 20000 elements from =a= and =b=, storing their sums in =c=. + +* Challenge #64 + +I was swamped with preparing for finals this weekend, so I decided to skip +challenge #64 in favor of something a bit more manageable. The question for this +one is, "[a]n array of array[x][y] form is accessed here. Try to determine the +dimensions of the array, at least partially, by finding y." + +#+BEGIN_SRC c +double f(double *array, int x, int y) +{ + return array[y + x * 15]; +} +#+END_SRC + +The array has some number of rows each containing 15 elements. + +* Challenge #65 + +#+BEGIN_SRC c +int f(int *array, int x, int y, int z) +{ + return array[z + 5 * 16 * (y + 4 * 15 * x)]; +} +#+END_SRC + +Assuming an array of integers, the dimensions of the array are 15 x 20 x ... + +* Challenge #74 + +#+BEGIN_SRC prog +jakob@Epsilon ~ $ sha256sum challenge74 +6d2ac11d1e6200d6a2cca988189764b6acdb7811d24619e8e66f1796c8c27394 challenge74 +jakob@Epsilon ~ $ file challenge74 +challenge74: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=7fa3bd4aa738bced5aaccb161090818646e07704, stripped +#+END_SRC + +#+BEGIN_SRC prog +2Z7A7-EK270-TMHR4-BHC71-CEB52-HELL0-HELL0-EONP9 +2Z7A7-6I7R9-MZGO9-FDQJ3-JN0Q6-HELL0-HELL0-72KJ9 +#+END_SRC + +#+BEGIN_SRC c +// WARNING: Could not reconcile some variable overlaps +// WARNING: [r2ghidra] Detected overlap for variable var_20h// WARNING: [r2ghidra] Failed to match type signed int64_t for variable var_10h to Decompiler type: Unknown type +// identifier signed +// WARNING: [r2ghidra] Detected overlap for variable var_ch +// WARNING: [r2ghidra] Failed to match type signed int64_t for variable var_8h to Decompiler type: Unknown type +// identifier signed +// WARNING: [r2ghidra] Detected overlap for variable var_8h +// WARNING: [r2ghidra] Failed to match type signed int for variable var_4h to Decompiler type: Unknown type identifier +// signed +// WARNING: [r2ghidra] Detected overlap for variable var_4h +// WARNING: [r2ghidra] Detected overlap for variable var_bh + +undefined8 main(uint32_t argc, char **argv) +{ + int64_t iVar1; + char cVar2; + int32_t iVar3; + int64_t in_FS_OFFSET; + int64_t var_30h; + int64_t var_24h; + int64_t var_8h; + + iVar1 = *(int64_t *)(in_FS_OFFSET + 0x28); + var_24h._0_4_ = argc; + sym.imp.puts("Crackme/keygenme by Dennis Yurichev, http://challenges.re/74"); + sym.imp.putchar(10); + if ((uint32_t)var_24h == 1) { + sym.imp.puts("Command line: <serial number>"); + // WARNING: Subroutine does not return + sym.imp.exit(0); + } + iVar3 = sym.imp.memcmp(argv[1] + 0x1e, "HELL0-HELL0", 0xb); + if (iVar3 != 0) { + sym.imp.puts("SN format is incorrect"); + // WARNING: Subroutine does not return + sym.imp.exit(0); + } + iVar3 = fcn.00400bb5((int64_t)argv[1], (int64_t)&var_24h + 4); + if (iVar3 == -1) { + sym.imp.puts("SN format is incorrect"); + // WARNING: Subroutine does not return + sym.imp.exit(0); + } + cVar2 = fcn.0040085e((void *)((int64_t)&var_24h + 4)); + if (cVar2 == '\0') { + sym.imp.puts("SN is not valid"); + } else { + sym.imp.puts("SN valid"); + } + if (iVar1 != *(int64_t *)(in_FS_OFFSET + 0x28)) { + // WARNING: Subroutine does not return + sym.imp.__stack_chk_fail(); + } + return 0; +} +#+END_SRC + +#+BEGIN_SRC c +#include <stdint.h> + +int main(int argc, char **argv) +{ + char buf[24]; + + printf("Crackme/keygenme by Dennis Yurichev, http://challenges.re/74\n\n"); + + if (argc == 1) { + puts("Command line: <serial number>"); + exit(0); + } + + if (memcmp(argv[1] + 0x1e, "HELL0-HELL0", 0xb)) { + puts("SN format is incorrect"); + exit(0); + } + + if (fcn_00400bb5(argv[1], buf) == -1) { + puts("SN format is incorrect"); + exit(0); + } + + if (fcn_0040085e(buf) == '\0') { + puts("SN is not valid"); + } else { + puts("SN valid"); + } + + return 0; +} +#+END_SRC + +- Removed iVar1, since that's just the stack canary. + +# For var_24h: +# +# pushq %rbp +# movq %rsp, %rbp +# subq $0x30, %rsp +# movl %edi, var_24h ; argc +# movq %rsi, var_30h ; argv +# +# Ended up removing it anyway. Using 'argc' is clearer. + +# leaq var_20h, %rdx +# movq %rdx, %rsi +# movq %rax, %rdi +# callq fcn.00400bb5 + +# leaq var_20h, %rax +# movq %rax, %rdi +# callq fcn.0040085e + +We can tell already that the sixth and seventh components must be "HELL0-HELL0". + +# From fcn_00400bb5 +#+BEGIN_SRC c +var_10h._0_4_ = 0; +while ((int32_t)var_10h < 7) { + if (*(char *)(arg1 + (int64_t)((int32_t)var_10h * 6) + 5) != '-') { + return 0xffffffff; + } + var_10h._0_4_ = (int32_t)var_10h + 1; +} +#+END_SRC + +# --- + +- =fcn_00400bb5= parses the key into a buffer (I renamed to =parse_key=) + - =charcode= function maps numerals to their numeric values ('0' becomes 0) and letters 'A' through 'Z' to 10-35 + - buffer is written with 3 bytes per component + - I thought this was a decompiler mistake at first, but I checked the disassembly and it's legit. +- =fcn_0040085e= enables features based on the parsed key (I renamed to =enable_features=) + - parsed buffer needs to start with 0xdeadbabe + - bytes 4 and 5 give a numerical year, 6 gives a numerical month, and 7 gives a numerical day. + - Sanity checks to ensure that day is between 1 and 31, that month is between 1 and 12, and that year is between 2016 and 2101. + - 7th bit of byte 8 enables feature A + - 1st bit of byte 9 enables feature B + - 2nd bit of byte 10 enables feature C + - 4th bit of byte 11 enables feature D + - 1st bit of byte 12 enables feature E + - One final check of the last 8 bytes against a checksum function +checksum(0, parsed, 0x18) + +#+BEGIN_SRC c +#include <stdint.h> + +uint64_t checksum(int64_t init, char *parsed, int64_t length) +{ + uint64_t ret; + char *cur; + int i; + int j; + + cur = parsed; + ret = ~init; + i = length; + + while (i != 0) { + ret = ret ^ (uint64_t) *cur; + j = 0; + while (i--, cur++, j < 8) { + if ((ret & 1) == 0) { + ret = ret >> 1; + } else { + ret = ret >> 1 ^ 0x42f0e1eb0badbad0; + } + j++; + } + } + + return ~ret; +} +#+END_SRC + +# --- + +From this, we can hack together a quick key verifier. + +#+BEGIN_SRC common-lisp +(use-package :cl-utilities) + +(defun charcode (c) + (let ((value (char-code c))) + (cond ((<= (char-code #\0) value (char-code #\9)) (- value #x30)) + ((<= (char-code #\A) value (char-code #\Z)) (- value #x37))))) + +(defun hash-component (component) + (let* ((characters (coerce component 'list)) + (values (mapcar #'charcode characters))) + (unless (or (/= 5 (length values)) (find nil values)) + (+ (* #x000001 (nth 0 values)) + (* #x000024 (nth 1 values)) + (* #x000510 (nth 2 values)) + (* #x00b640 (nth 3 values)) + (* #x19a100 (nth 4 values)))))) + +(defun parse-key (key) + (reduce #'append + (mapcar #'(lambda (component) + (let ((hash (hash-component component))) + (list (logand hash #xff) + (logand (ash hash -8) #xff) + (logand (ash hash -16) #xff)))) + (split-sequence #\- key)))) + +(defun key-valid-p (key) + ;; Must begin with 0xdeadbabe, and have HELL0 for components 6 and 7. + (and (equal (subseq key 0 4) '(222 173 186 190)) + (equal (subseq key 15 21) '(153 95 15 153 95 15)))) + +(defun key-attributes (key) + (let ((parsed (parse-key key))) + (list :valid (key-valid-p parsed) + :expiry-year (logior (ash (nth 4 parsed) 8) + (nth 5 parsed)) + :expiry-month (nth 6 parsed) + :expiry-day (nth 7 parsed) + :feature-a (plusp (logand (nth 8 parsed) (ash 1 6))) + :feature-b (plusp (logand (nth 9 parsed) (ash 1 0))) + :feature-c (plusp (logand (nth 10 parsed) (ash 1 1))) + :feature-d (plusp (logand (nth 11 parsed) (ash 1 2))) + :feature-e (plusp (logand (nth 12 parsed) (ash 1 0)))))) + +(key-attributes "2Z7A7-EK270-TMHR4-BHC71-CEB52-HELL0-HELL0-EONP9") +;; (:VALID T :EXPIRY-YEAR 2053 :EXPIRY-MONTH 5 :EXPIRY-DAY 22 :FEATURE-A T +;; :FEATURE-B T :FEATURE-C T :FEATURE-D T :FEATURE-E NIL) + +(key-attributes "2Z7A7-6I7R9-MZGO9-FDQJ3-JN0Q6-HELL0-HELL0-72KJ9") +;; (:VALID T :EXPIRY-YEAR 2042 :EXPIRY-MONTH 2 :EXPIRY-DAY 21 :FEATURE-A T +;; :FEATURE-B T :FEATURE-C T :FEATURE-D T :FEATURE-E T) +#+END_SRC + +And verify our results. + +#+BEGIN_SRC prog +jakob@Epsilon ~ $ ./challenge74 "2Z7A7-EK270-TMHR4-BHC71-CEB52-HELL0-HELL0-EONP9" +Crackme/keygenme by Dennis Yurichev, http://challenges.re/74 + +Expiration date: 2053-05-22 +Feature A: ON +Feature B: ON +Feature C: ON +Feature D: ON +Feature E: OFF +SN valid +jakob@Epsilon ~ $ ./challenge74 "2Z7A7-6I7R9-MZGO9-FDQJ3-JN0Q6-HELL0-HELL0-72KJ9" +Crackme/keygenme by Dennis Yurichev, http://challenges.re/74 + +Expiration date: 2042-02-21 +Feature A: ON +Feature B: ON +Feature C: ON +Feature D: ON +Feature E: ON +SN valid +#+END_SRC + +# --- + +Tried to brute force overnight, which brought me back to playing CTF, but it was +sadly unfruitful. + +# --- + +GDB's call functionality is godsend. + +(gdb) p (unsigned long long) $checksum(0, &{'\xff', '\xff', '\xff'}, 3) +$14 = 18446742974197923840 + +# --- + +Z3 https://jomo.tv/security/mrmcd-ctf-writeup-flag-checker +https://ericpony.github.io/z3py-tutorial/guide-examples.htm +https://stackoverflow.com/questions/53726998/using-z3-where-constraint-depends-on-output-of-function + +Right shift was giving me a hard time (not prepending with zeroes) which you can +see from the & 0x7fffffffffffffff. + +#+BEGIN_SRC prog +jakob@Epsilon ~ $ python solver.py +[bv22 = 196, + bv13 = 18, + bv21 = 216, + bv23 = 130, + bv14 = 209, + bv8 = 130, + bv10 = 108, + bv9 = 170, + bv11 = 208, + bv12 = 240, + bv20 = 15, + bv19 = 95, + bv18 = 153, + bv17 = 15, + bv16 = 95, + bv15 = 153, + bv7 = 24, + bv6 = 12, + bv5 = 227, + bv4 = 0, + bv3 = 190, + bv2 = 186, + bv1 = 173, + bv0 = 222] +#+END_SRC + +#+BEGIN_SRC common-lisp +(string-join (mapcar #'ahash-component-inverse (mapcar #'triplet-to-number '((222 173 186) (190 0 27) (12 24 13) (170 108 208) (240 18 20) (153 95 15) (153 95 15) (216 196 130)))) "-") +#+END_SRC + +#+BEGIN_SRC prog +CL-USER> (key-attributes "2Z7A7-AHX11-S4EI0-6LR48-K37S0-HELL0-HELL0-KPO35") +(:VALID NIL :EXPIRY-YEAR 27 :EXPIRY-MONTH 12 :EXPIRY-DAY 24 :FEATURE-A NIL + :FEATURE-B NIL :FEATURE-C NIL :FEATURE-D NIL :FEATURE-E NIL) +#+END_SRC + +OH MY GOD + +#+BEGIN_SRC python +... +s.add(key[4] == 2019 & 0xff00) +... +#+END_SRC + +That should've been =s.add(key[4] == ((2019 & 0xff00) >> 8))=... + +;-; + +Let's try this again... + +#+BEGIN_SRC prog +jakob@Epsilon ~ $ python solver.py +[bv22 = 80, + bv13 = 39, + bv21 = 204, + bv23 = 133, + bv14 = 124, + bv8 = 140, + bv10 = 12, + bv9 = 168, + bv11 = 183, + bv12 = 184, + bv20 = 15, + bv19 = 95, + bv18 = 153, + bv17 = 15, + bv16 = 95, + bv15 = 153, + bv7 = 25, + bv6 = 12, + bv5 = 227, + bv4 = 7, + bv3 = 190, + bv2 = 186, + bv1 = 173, + bv0 = 222] +#+END_SRC + +#+BEGIN_SRC prog +CL-USER> (mapcar #'hash-component-inverse (mapcar #'triplet-to-number '((222 173 186) (190 7 227) (12 25 140) (168 12 183) (184 39 124) (153 95 15) (153 95 15) (204 80 133)))) +("2Z7A7" "YFWU8" "CGSG5" "CF457" "K9EU4" "HELL0" "HELL0" "OH975") +CL-USER> (key-attributes "2Z7A7-YFWU8-CGSG5-CF457-K9EU4-HELL0-HELL0-OH975") +(:VALID T :EXPIRY-YEAR 2019 :EXPIRY-MONTH 12 :EXPIRY-DAY 25 :FEATURE-A NIL + :FEATURE-B NIL :FEATURE-C NIL :FEATURE-D NIL :FEATURE-E NIL) +#+END_SRC + +#+BEGIN_SRC prog +jakob@Epsilon ~ $ ./challenge74 "2Z7A7-YFWU8-CGSG5-CF457-K9EU4-HELL0-HELL0-OH975" +Crackme/keygenme by Dennis Yurichev, http://challenges.re/74 + +Expiration date: 2019-12-25 +Feature A: OFF +Feature B: OFF +Feature C: OFF +Feature D: OFF +Feature E: OFF +SN valid +#+END_SRC + +# --- + +I have to say, r2ghidra is really fucking good. =pdg= takes a while, but if you +rename some variables with =afvn= and run it again, it spits out the updated +version instantly (probably doing some sort of caching/quick substitution). + +# --- + +# End-of-year reflection: Context is super important for making sense of assembly code. +# Case in point: crypto challenges like #33 +# Idea to toy with: A monthly crackme, hints given out every week and solutions posted (also, the challenges won't be painfully abstract) |