28,99 €
The Assembly language is the lowest level human readable programming language on any platform. Knowing the way things are on the Assembly level will help developers design their code in a much more elegant and efficient way. It may be produced by compiling source code from a high-level programming language (such as C/C++) but can also be written from scratch. Assembly code can be converted to machine code using an assembler.
The first section of the book starts with setting up the development environment on Windows and Linux, mentioning most common toolchains. The reader is led through the basic structure of CPU and memory, and is presented the most important Assembly instructions through examples for both Windows and Linux, 32 and 64 bits. Then the reader would understand how high level languages are translated into Assembly and then compiled into object code. Finally we will cover patching existing code, either legacy code without sources or a running code in same or remote process.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 323
Veröffentlichungsjahr: 2017
BIRMINGHAM - MUMBAI
Copyright © 2017 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
First published: September 2017
Production reference: 1220917
ISBN 978-1-78728-748-8
www.packtpub.com
Author
Alexey Lyashko
Copy Editor
Pranjali Chury
Reviewer
Tomasz Grysztar
Project Coordinator
Vaidehi Sawant
Commissioning Editor
Merint Mathew
Proofreader
Safis Editing
Acquisition Editor
Karan Sadawana
Indexer
Francy Puthiry
Content Development Editor
Zeeyan Pinheiro
Graphics
Abhinash Sahu
Technical Editor
Vivek Pala
Production Coordinator
Nilesh Mohite
Alexey Lyashko is an Assembly language addict, independent software reverse engineer, and consultant. At the very beginning of his career, when he was a malware researcher at Aladdin Knowledge Systems, he invented and developed a generic code recognition method known as HOFA™. After spending a few years in the anti-malware industry and gaining sufficient experience in low-level development and reverse engineering, Alexey switched to content protection and worked as a reverse engineering consultant with Irdeto’s BD+ department, actively participating in content protection technology development. Since 2013, he has worked with several software development companies providing reverse engineering and low-level software development consultancy.
Tomasz Grysztar is a self-employed programmer and systems designer, with a focus on machine languages. He is the author of FASM, one of the assemblers for the x86 architecture of processors, and he has been continuously developing it for nearly 20 years.
For support files and downloads related to your book, please visit www.PacktPub.com.
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.
https://www.packtpub.com/mapt
Get the most in-demand software skills with Mapt. Mapt gives you full access to all Packt books and video courses, as well as industry-leading tools to help you plan your personal development and advance your career.
Fully searchable across every book published by Packt
Copy and paste, print, and bookmark content
On demand and accessible via a web browser
Thanks for purchasing this Packt book. At Packt, quality is at the heart of our editorial process. To help us improve, please leave us an honest review on this book's Amazon page at https://www.amazon.com/dp/1787287483.
If you'd like to join our team of regular reviewers, you can e-mail us at [email protected]. We award our regular reviewers with free eBooks and videos in exchange for their valuable feedback. Help us be relentless in improving our products!
Preface
What this book covers
What you need for this book
Who this book is for
Conventions
Reader feedback
Customer support
Downloading the example code
Errata
Piracy
Questions
Intel Architecture
Processor registers
General purpose registers
Accumulators
Counter
Stack pointer
Source and destination indices
Base pointer
Instruction pointer
Floating point registers
XMM registers
Segment registers and memory organization
Real mode
Protected mode - segmentation
Protected mode - paging
Long mode - paging
Control registers
Debug registers
Debug address registers DR0 - DR3
Debug control register (DR7)
Debug status register (DR6)
The EFlags register
Bit #0 - carry flag
Bit #2 - parity flag
Bit #4 - adjust flag
Bit #6 - zero flag
Bit #7 - sign flag
Bit #8 - trap flag
Bit #9 - interrupt enable flag
Bit #10 - direction flag
Bit #11 - overflow flag
Remaining bits
Summary
Setting Up a Development Environment
Microsoft Macro Assembler
Installing Microsoft Visual Studio 2017 Community
Setting up the Assembly project
GNU Assembler (GAS)
Installing GAS
Step 1 - installing GAS
Step 2 - let's test
Flat Assembler
Installing the Flat Assembler
The first FASM program
Windows
Linux
Summary
Intel Instruction Set Architecture (ISA)
Assembly source template
The Windows Assembly template (32-bit)
The Linux Assembly template (32-bit)
Data types and their definitions
A debugger
The instruction set summary
General purpose instructions
Data transfer instructions
Binary Arithmetic Instructions
Decimal arithmetic instructions
Logical instructions
Shift and rotate instructions
Bit and byte instructions
Execution flow transfer instructions
String instructions
ENTER/LEAVE
Flag control instructions
Miscellaneous instructions
FPU instructions
Extensions
AES-NI
SSE
Example program
Summary
Memory Addressing Modes
Addressing code
Sequential addressing
Direct addressing
Indirect addressing
RIP based addressing
Addressing data
Sequential addressing
Direct addressing
Scale, index, base, and displacement
RIP addressing
Far pointers
Summary
Parallel Data Processing
SSE
Registers
Revisions
Biorhythm calculator
The idea
The algorithm
Data section
The code
Standard header
The main() function
Data preparation steps
Calculation loop
Adjustment of sine input values
Computing sine
Exponentiation
Factorials
AVX-512
Summary
Macro Instructions
What are macro instructions?
How it works
Macro instructions with parameters
Variadic macro instructions
An introduction to calling conventions
cdecl (32-bit)
stdcall (32-bit)
Microsoft x64 (64-bit)
AMD64 (64-bit)
A note on Flat Assembler's macro capabilities
Macro instructions in MASM and GAS
Microsoft Macro Assembler
The GNU Assembler
Other assembler directives (FASM Specific)
The conditional assembly
Repeat directives
Inclusion directives
The include directive
File directive
Summary
Data Structures
Arrays
Simple byte arrays
Arrays of words, double words, and quad words
Structures
Addressing structure members
Arrays of structures
Arrays of pointers to structures
Linked lists
Special cases of linked lists
Stack
Queue and deque
Priority queues
Cyclic linked list
Summary for special cases of linked lists
Trees
A practical example
Example - trivial cryptographic virtual machine
Virtual machine architecture
Adding support for a virtual processor to the Flat Assembler
Virtual code
The virtual processor
Searching the tree
The loop
Tree balancing
Sparse matrices
Graphs
Summary
Mixing Modules Written in Assembly and Those Written in High-Level Languages
Crypto Core
Portability
Specifying the output format
Conditional declaration of code and data sections
Exporting symbols
Core procedures
Encryption/decryption
Setting the encryption/decryption parameters
f_set_data_pointer
f_set_data_length
GetPointers()
Interfacing with C/C++
Static linking - Visual Studio 2017
Static linking - GCC
Dynamic linking
Assembly and managed code
Native structure versus managed structure
Importing from DLL/SO and function pointers
Summary
Operating System Interface
The rings
System call
System call hardware interface
Direct system calls
Indirect system calls
Using libraries
Windows
Linking against object and/or library files
Object file
Producing the executable
Importing procedures from DLL
Linux
Linking against object and/or library files
Object file
Producing the executable
Dynamic linking of ELF
The code
Summary
Patching Legacy Code
The executable
The issue
PE files
Headers
Imports
Gathering information
Locating calls to gets()
Preparing for the patch
Importing fgets()
Patching calls
Shim code
Applying the patch
A complex scenario
Preparing the patch
Adjusting file headers
Appending a new section
Fixing the call instruction
ELF executables
LD_PRELOAD
A shared object
Summary
Oh, Almost Forgot
Protecting the code
The original code
The call
The call obfuscation macro
A bit of kernel space
LKM structure
LKM source
.init.text
.exit.text
.rodata.str1.1
.modinfo
.gnu.linkonce.this_module
__versions
Testing the LKM
Summary
The Assembly language is the lowest-level human readable programming language on any platform. Knowing the way things are on the Assembly level will help developers design their code in a much more elegant and efficient way.
Unfortunately, the modern world of software development does not require deep understanding of how programs are executed on the low level, not to mention the number of scripting languages and different frameworks that are there to ease the process of software development, and which are often mistakenly treated as inefficient mostly because developers think that the framework/scripting engine should cope with the lameness of the code. The intent behind this book is to show how important it is to understand the basics, which are too often left behind a developer’s learning curve.
The Assembly language is a powerful tool that developers may use in their projects to gain more efficiency with their code, not to mention that Assembly is the basis of computing even in today's world of high-level languages, software frameworks, and scripting engines. The core idea behind this book is to familiarize software developers with things that are often skipped or are not given enough attention by developers and, much worse, by those who teach them. It may be hard to believe that the Assembly language itself is only the tip of the iceberg (unfortunately, the part of the iceberg that is hidden in water falls outside the scope of this book), but even it alone may highly improve your ability to develop much cleaner, more elegant and, more importantly, much more efficient code.
Chapter 1, Intel Architecture, provides a brief insight into the Intel architecture, covering processor registers and their usage.
Chapter 2, Setting Up a Development Environment, contains detailed instructions on setting up a development environment for programming in Assembly.
Chapter 3, Intel Instruction Set Architecture (ISA), introduces you to the instruction set of Intel processors.
Chapter 4, Memory Addressing Modes, gives an overview of the many memory addressing modes supported by Intel processors.
Chapter 5, Parallel Data Processing, is dedicated to the Intel architecture extensions that add support for parallel processing of multiple data.
Chapter 6, Macro Instructions, provides an introduction to one of the most powerful features of modern assemblers--their support for macro instructions.
Chapter 7, Data Structures, helps us organize data properly as there isn't much that we can do with it.
Chapter 8, Mixing Modules Written in Assembly and Those Written in High-Level Languages, gives a description of the various methods of interfacing our Assembly code with the outer world.
Chapter 9, Operating System Interface, gives you a way to discover how programs written in Assembly may interact with Windows and Linux operating systems.
Chapter 10, Patching Legacy Code, attempts to show the basics of patching existing executables, which is an art in itself.
Chapter 11, Oh, Almost Forgot, covers a few things that did not fit into any of the preceding chapters but are, nevertheless, interesting and may even be important.
The requirements for this book are rather minimal. All you need is a computer running either Windows or Linux and the desire to learn new things.
This book is primarily intended for developers wishing to enrich their understanding of low-level proceedings, but, in fact, there is no special requirement for much experience, although a certain level of experience is anticipated. Of course, anyone interested in Assembly programming should be able to find something useful in this book.
Feedback from our readers is always welcome. Let us know what you think about this book-what you liked or disliked. Reader feedback is important for us as it helps us develop titles that you will really get the most out of. To send us general feedback, simply e-mail [email protected], and mention the book's title in the subject of your message. If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide at www.packtpub.com/authors.
Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.
You can download the example code files for this book from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you. You can download the code files by following these steps:
Log in or register to our website using your e-mail address and password.
Hover the mouse pointer on the
SUPPORT
tab at the top.
Click on
Code Downloads & Errata
.
Enter the name of the book in the
Search
box.
Select the book for which you're looking to download the code files.
Choose from the drop-down menu where you purchased this book from.
Click on
Code Download
.
Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:
WinRAR / 7-Zip for Windows
Zipeg / iZip / UnRarX for Mac
7-Zip / PeaZip for Linux
The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Mastering-Assembly-Programming. We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!
Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books-maybe a mistake in the text or the code-we would be grateful if you could report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded to our website or added to any list of existing errata under the Errata section of that title. To view the previously submitted errata, go to https://www.packtpub.com/books/content/support and enter the name of the book in the search field. The required information will appear under the Errata section.
Piracy of copyrighted material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works in any form on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy. Please contact us at [email protected] with a link to the suspected pirated material. We appreciate your help in protecting our authors and our ability to bring you valuable content.
If you have a problem with any aspect of this book, you can contact us at [email protected], and we will do our best to address the problem.
When speaking about the Assembly language, people usually imagine a sort of unknown and dangerous beast, which obeys only the weirdest representatives of the programming community, or a gun that may only be used for shooting your own leg. Just as any prejudice, this one is rooted in ignorance and the primal fear of the unknown. The purpose of this book is not only to help you overcome this prejudice, but also to show how the Assembly language may become a powerful tool, a sharp lancet, that will help you perform certain tasks, even sophisticated ones, with elegance and relative simplicity, avoiding the unnecessary complications which are, sometimes, implied by high-level languages.
First of all, what is the Assembly language? To put it simply and precisely, we may safely define the Assembly language as symbolic or human readable machine code as each Assembly instruction translates into a single machine instruction (with a few exceptions). To be even more precise, there is no such thing as a single Assembly language as, instead, there are numerous Assembly languages--one per platform, where a platform is a programmable device. Almost any programmable device with a certain instruction set may have its own Assembly language, but this is not always so. Exceptions are devices such as, for example, NAND flash chips, which have their own command set, but have no means for fetching instructions from memory and executing them without implicitly being told to do so.
In order to be able to effectively use the Assembly language, one has to have a precise understanding of the underlying platform, as programming in the Assembly language means "talking" directly to the device. The deeper such understanding is, the more efficient is Assembly programming; however, we are not going to look at this in great detail, as this is beyond the scope of the book. One book would not be enough to cover each and every aspect of the specific architecture. Since we are going to concentrate on the Intel architecture during the course of this book, let's try to obtain at least a general understanding of Intel's x86/AMD64 architectures, and try to enrich it and make it deeper.
This chapter, in particular, covers processor registers and the functionality thereof and briefly describes memory organization (for example, segmentation and paging).
General purpose registers
: Despite the fact that some of them have special meanings under certain circumstances, these registers, as the name of the group states, may be used for any purpose.
Floating point registers
: These registers are used for floating point operations.
Segment registers
: These registers are hardly accessed by applications (the most common case is setting up structured exception handlers on Windows); however, it is important to cover them here so we may better understand the way the CPU percives RAM. The part of the chapter that discusses segment registers also addresses a few memory organization aspects, such as segmentation and paging.
Control registers
: This is a tiny group of registers of registers of high importance, as they control the behavior of the processor as well as enable or disable certain features.
Debug registers
: Although registers of this group are mainly used by debuggers, they add some interesting abilities to our code, for example the ability to set hardware breakpoints when tracing execution of a program.
EFlags register
: This is also known as the status register on some platforms. This one provides us with the information regarding the result of the latest
arithmetic logic unit
(
ALU
) operation performed, as well as some settings of the CPU itself.
Each programmable device, and Intel processors are not an exception, has a set of general purpose registers--memory cells located physically on the die, thus providing low latency access. They are used for temporary storage of data that a processor operates on or data that is frequently accessed (if the amount of general purpose registers allows this). The amount and bit size of registers on an Intel CPU vary in accordance with the current mode of operation. An Intel CPU has at least two modes:
Real mode
: This is the good old DOS mode. When the processor is powered up, it starts in the real mode, which has certain limitations, such as the size of the address bus, which is only 20 bits, and the segmented memory space.
Protected mode
: This was first introduced in 80286. This mode provides access to larger amount of memory, as it uses different memory segmentation mechanisms. Paging, introduced in 80386, allows even easier memory addressing virtualization.
Since about 2003, we also have the so-called long mode--64-bit registers/addressing (although, not all 64 bits are used for addressing yet), flat memory model, and RIP-based addressing (addressing relative to the instruction pointer register). In this book, we will work with 32-bit protected (there is such a thing as the 16-bit protected mode, but that is out of scope) and Long, which is a 64-bit mode of operation. The long mode may be considered a 64-bit extension of the protected mode, which evolved from 16-bit to 32-bit. It is important to know that registers accessible in the earlier mode are also accessible in the newer mode, meaning that the registers that were accessible in the real mode are also accessible in the protected mode, and that registers accessible in the protected mode would also be accessible in the long mode (if long mode is supported by the processor). There are a few exceptions regarding the bit width of certain registers and we will look at this soon in this chapter. However, since 16-bit modes (real and 16-bit protected modes) are no longer used by application developers (with minor possible exceptions), in this book, we will work on protected and long modes only.
Depending on the mode of the operation (protected or long), there are 8 to 16 available general purpose registers in modern Intel processors. Each register is divided into subregisters, allowing access to data with a bit width lower than the width of the register.
The following table shows general purpose registers (further referred to as GPR):
For convenience, we will refer to the registers by their 32-bit names (such as EAX, EBX, and so on) when we do not need to explicitly refer to a register of a certain bit width. The preceding table shows all general purpose registers available on the Intel platform. Some of them are only available in the long mode (all 64-bit registers, R* registers, and a few of the 8-bit registers) and certain combinations are not allowed. However, despite the fact that we can use those registers for any purpose, some of them do have a special meaning in certain circumstances.
An ESP register is the stack pointer. This register, together with the SS register (the SS register is explained a bit later in this chapter), describes the stack area of a thread, where SS contains the descriptor of the stack segment and ESP is the index that points to the current position within the stack.
ESI and EDI registers serve as source and destination index registers in string operations, where ESI contains the source address and EDI, obviously, the destination address. We will talk about these registers a bit more in Chapter 3, Intel Instruction Set Architecture (ISA).
EBP. This register is called the base pointer as its most common use is to point to the base of a stack frame during function calls. However, unlike the previously discussed registers, you may use any other register for this purpose if needed.
Another register worth mentioning here is EBX, which, in the good old days of 16-bit modes (when it was still just a BX register), was one of the few registers that we could use as a base for addressing. Unlike EBP, EBX was (in the case of the XLAT instruction, which by default uses DS:EBX, still is) intended to point to a data segment.
There is one more special register that cannot be used for data storage--EIP (IP in the real mode or RIP in the long mode). This is the instruction pointer and contains the address of the instruction after the instruction currently being executed. All instructions are implicitly fetched from the code segment by the CPU; thus the full address of the instruction following the one being executed should be described as CS:IP. Also, there is no regular way to modify its content directly. It is not impossible, but we can't just use a mov instruction in order to load a value into EIP.
All the other registers have no special meaning from the processor's perspective and may be used for any purpose.
The CPU itself has no means for floating point arithmetic operations. In 1980, Intel announced the Intel 8087 - the floating point coprocessor for the 8086 line. 8087 remained as a separate installable device until 1989, when Intel came up with the 80486 (i486) processor, which had an integrated 8087 circuit. However, when talking about floating point registers and floating point instructions, we still refer to 8087 as a floating-point unit (FPU) or, sometimes, still as a floating-point coprocessor (however, the latter is becoming more and more rare).
8087 has eight registers, 80 bits each, arranged in a stack fashion, meaning that operands are pushed onto this stack from the memory and results are popped from the topmost register to the memory. These registers are named ST0 to ST7 (ST--stack) and the most used one, that is, the ST0 register, may be referred to as simply ST.
The floating-point coprocessor supports several data types:
80-bit extended-precision real
64-bit double-precision real
32-bit single-precision real
18-digit decimal integer
64-bit binary integer
32-bit binary integer
16-bit binary integer
The floating-point coprocessor will be discussed in more detail in Chapter 3, Intel Instruction Set Architecture (ISA).
The 128-bit XMM registers are part of the SSE extension (where SSE is short for Streaming SIMD Extension, and SIMD, in turn, stands for single instruction multiple data). There are eight XMM registers available in non -64-bit modes and 16 XMM registers in long mode, which allow simultaneous operations on:
16 bytes
eight words
four double words
two quad words
four floats
two doubles
We will pay much more attention to these registers and the technology behind them in Chapter 5, Parallel Data Processing.
Memory organization is one of the most important aspects of CPU design. The first thing to note is that when we say "memory organization", we do not mean its physical layout on memory chips/boards. For us, it is much more important how the CPU sees memory and how it communicates with it (on a higher level, of course, as we are not going to dive into the hardware aspects of the architecture).
However, as the book is dedicated to application programming, rather than operating system development, we will further consider the most relevant aspects of memory organization and access in this section.
While it was all fine and simple in real mode, things become a bit more complicated when it comes to protected mode. Unfortunately, memory segmentation is still intact, but the segment register no longer contain addresses. Instead, they are loaded with the so-called selectors, which are, in turn, the indices into the descriptor table multiplied by 8 (shifted 3 bits to the left). The two least significant bits designate the requested privilege level (0 for kernel space to 3 for user land). The third bit (at index 2) is the TI bit (table indicator), which indicates whether the descriptor being referred is in a global descriptor table (0) or in a local descriptor table (1). The memory descriptor is a tiny 8-byte structure, which describes the range of physical memory, its access rights, and some additional attributes:
Descriptors are stored in at least two tables:
GDT
: Global descriptor table (used by the operating system)
LDT
: Local descriptor table (per task descriptor table)
As we may conclude, the organization of memory in protected mode is not that different from that in real mode after all.
There are other types of descriptors--interrupt descriptors (stored in the interrupt description table (IDT)) and system descriptors; however, since these are in use in kernel space only, we will not discuss them, as that falls out of the scope of this book.