diff options
| author | Jakob L. Kreuze <zerodaysfordays@sdf.lonestar.org> | 2019-12-28 20:34:37 -0500 |
|---|---|---|
| committer | Jakob L. Kreuze <zerodaysfordays@sdf.lonestar.org> | 2019-12-28 20:34:37 -0500 |
| commit | 7b2e58eafb5562ca425075e112746b0ab33a0869 (patch) | |
| tree | f5b17b5b58d1d578a27bcca5ab1fc0a533eb93b2 /org | |
| parent | 216ca5aa5a3b5f82908414fdab591a0e6a6ca9d9 (diff) | |
Let's try this again...
Diffstat (limited to 'org')
9 files changed, 2133 insertions, 287 deletions
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) |