Let’s Learn x86-64 Assembly: Part 0 – Setup and First Steps
gpfault.net
gpfault.net
I'd probably recommend getting some ARM or ARM64 board and starting with this, most of the concepts will carry over to other assemblies anyway. Writing a simple CPU emulator for some simple architecture and then coding on it can also be a great teaching experience, if a little more involved.
I find this tutorial a bit oddly structured too, but that may be because I'm from un*x world and I don't have the Windows mindset. It basically goes from "int3 ret" to "The PE Format and DLL Imports". That seems like quite the jump, and not necessarily super relevant to learning ASM IMO.
I think it would make more sense to start with a simpler language and work your way to C++ if that's what you want.
Agreed. As someone who did lots of assembly for the 6502 and the whole Motorola 68k series CPUs while on Mac & Commodore-machines, I thought I might as well learn x86 assembly when I moved to "regular" PCs.
After a week or so of en-bafflement, I just decided that dealing with assembly on any sort of regular basis was not something I was interested in doing on the Intel-platform.
It was inferior to competing platforms in almost every conceivable way back then (except performance) and it haven't improved since. It's just utterly terrible and the only reason it's still around is backwards compatibility. Nobody would design anything like this today.
For me jumping from z80 -> x86 was only a small change. The same notion of paired-registers and very similar instructions made it a simple enough change.
Of course these days there have been a lot of changes, but the basics are still basic, albeit idiosyncratic.
But even more generally I think you won't have any trouble picking up x86 idiosyncrasies if you've familiarized yourself with ARM/MIPS/Z80 assembly. Although maybe MIPS would be a bad choice because it doesn't use flags which are an important concept for many assembly languages.
IMO the key concepts for an assembler tutorial would be, out of the top of my head:
- The stack, - banking registers, the frame pointer, - The various types of jumps/calls/branches and their differences, - Conditionals, - Calling conventions, - Banking/context switching/IRQs (at least for low level programming, not so important if you're only dealing with userland I suppose),
That stuff exists on basically any architecture. Then you have things like immediate encoding and addressing modes which are also very important and architecture-specific.
I suppose SIMD could be interesting as well, but these days it seems to be mostly done with intrinsics instead of raw assembly, at least in my experience.
E.g., knowing what _main is and how to call an OS primitive is a lot more important when reverse engineering than knowing about context switching and simd. I was just trying to say, that's where the focus on a specific OS comes from, and a focus on things that people who do manual numerical optimization would consider irrelevant or at best tangential to what they consider 'important' in assembly.
Although no obvious example is springing to mind (any help?), I'm pretty sure there are cases where it's faster to teach someone—even an adult!—a https://en.wikipedia.org/wiki/Lie-to-children model of a system, and then teach them the actual system, than it is to teach them the actual system from the start.
I believe that, for the same reason I believe that people get better at the reflex skills of a video game if early parts of the game hold back some of the game's mechanics, focusing on only a core subset. You're allowed to develop just those sub-skills in isolation, and get good at them, so that they can become subconscious-enough that you won't be distracted thinking about them any more by the time the new sub-skills are introduced.
Honestly, I think a great way to learn bare-metal programming would be to not work with bare-metal at all, but rather to work with a virtual machine for a custom ISA, where that supported ISA has 100 different sub-variants (ranging from very simplified to very "realistic") and the VM supports all of them. You'd learn to work with the simplest ISA (e.g. target a compiler backend to it), then learn the next-simplest (and tweak your compiler to also emit the new instructions introduced), etc. Evolve from RISC to CISC, from stack-based to register-based, from SISD to SIMD, gradually add vector instructions, etc.
By the end of a process like that, you'd understand a (fake) ISA nearly as complex as x86-64; but more importantly, you'd understand the why of it, not just the what. You'd understand the history behind each instruction, and why it has the options/limits it does. At that point, learning x86-64, or AArch64, or whatever else, would just be "learning the vocabulary", with no new skills per se.
If you like an easy instruction set and don't care about platform specific syscalls, I would recommend an 8bit µC. An Arduino for example.
Maybe not everybody shares my deeply rooted dislike for x86 though.
https://github.com/mlang/jonesforth
And yes, I know there is also jonesforth64. However, doing the porting myself was totally worth it.
That’s bits 8-15 (lowest being 0 and highest being 63), not the highest 8 bits
Didn’t have time to read the whole thing, but it looks nice.
|63|62|61|60|...|34|33|32|31|30|29|...|18|17|16|15|14|...|10|09|08|07|06|05|04|03|02|01|00|
|.............................................|.............................|<-- AH (8 bits) --->|<------- AL (8 bits) -------->|
|.............................................|.............................|<------------- AX (16 bits - lower part) --------->|
|.............................................|<---------------------------- EAX (32 bits - lower part) ------.---------->|
|<------------------------------------------------ RAX (full register - 64 bits) --------------------------.------>|
(10 thousands edits, hope you guys see the same as I see, perfectly aligned)
|63|62|...|33|32|31|30|...|17|16|15|14|...|09|08|07|06|...|01|00|
|...............|...............|<-AH (8 bits)->|<-AL (8 bits)->|
|...............|...............|<-AX (16 bits - lower part) -->|
|...............|<--------- EAX (32 bits - lower part) -------->|
|<--------------- RAX (full register - 64 bits) --------------->|
(This still won't look good on a mobile device because of the width, best viewed in a desktop browser.)> Additionally, the higher 8 bits of ax, bx, cx and dx can be referred to as ah, bh, ch and dh.
So either “higher” or “high” would work.
How?
GDB
Combined with
the TUI window and typing:
* layout asm
* layout regs
* focus cmd
And then ni and si were my favorite "step into instruction" and "next instruction" commands.
And for C the same thing but then simply with layout src and sometimes layout asm and layout regs as well.
1. https://www.cs.helsinki.fi/group/titokone/v1.100/kayttoohje/...
I find it interesting the author uses FASM, and I've seen it used a bit more than I did in the past. Several years ago I toyed with it and found it neat because the editor and all the samples it shipped with. It did seems a bit different from things like nasm or gas as it the FASM code I saw used all sorts of interesting macros that provided quasi-high-level constructs like if statements.
Also take a look at https://en.wikipedia.org/wiki/Return-oriented_programming