During hardware-oriented engagements, we are sometimes faced with a hardware device's firmware image. This may happen because we downloaded a firmware upgrade image to try to understand a device with a view of finding security flaws, or we may find an unknown device and managed to obtain a flash dump using a security weakness. In any case, knowing how to handle these images can lead to a good understanding of how the device works, and in some cases to a way to get a shell in the device.
This article series will teach you the basics of Linux-based flash memory devices. After a few basic concepts, we'll create our own simulated flash device in part 2 to learn how things work. Once we understand how a device works and how to create one, we'll build on this knowledge and learn how to reverse engineer flash images.
Before getting into Linux, we’ll give a brief overview of how flash memory works. While not comprehensive, this explanation will give a rough idea on how it works for a solid jumping off point.
Let's start with how a one-bit flash memory cell works. Flash memory is a type of non-volatile memory composed of cells similar to a field-effect transistor (FET). Each cell can be made to conduct by providing a voltage to a control gate, like an FET, by opening a conducting channel into the substrate. However, unlike a regular FET, the control gate also has a floating part (a part that is not connected to anything), which is how the cell is able to retain data.
Image 1: Flash memory cell.
In the diagram, we can see a source and drain and control gate, just like in a FET. The source line (usually grounded) is connected to the source, while the bit line is connected to the drain. The cell is controlled by the control gate, in the center.
Up to this point, we have essentially described a field effect transistor. The parameters of this transistor are tightly controlled, ensuring that it won't conduct until the control gate reaches a pre-defined voltage (VT1). However, the presence of the floating gate can turn this transistor into a memory cell. For a moment, let's assume we have managed to put a negative charge into this gate, as seen in the image below, in the right pane. This charge will partially cancel the effects of the control gate, and a higher voltage (VT2) will now have to be applied to make the transistor conduct. Since this charge cannot escape (as the floating gate is isolated), the effect is permanent.
Image 2: Charged and uncharged Flash memory cells. The charge in the floating gate counteracts the stimulus applied to the control gate.
In a sense, the behavior of this transistor indicates the state of the floating gate. If the cell conducts at VT1, we know the floating gate has no charge (a value of 1), while if it doesn't conduct until we reach VT2, we know there is a charge (a value of zero). Basically, by feeding more than VT1 but less than VT2 volts (also called the readout voltage) to the control gate, we can read the contents of the floating gate.
However, the floating gate is, well, floating. How do we get a charge into it, and how do we remove it?
The solution to this problem is quantum mechanics. Particles do not have a definite position in space but have a probability of being at a certain place. This means that using quantum effects, we could tunnel electrons into the floating gate and out. This cannot happen during normal operation, or our memory would become volatile. The trick is designing a barrier that cannot be overcome at usual energies, but that can be tunneled if the electrons are energetic enough.
To program a cell, we apply a high voltage to both the control gate and the drain. For example, in the case of a 1.8V cell, 12V can be considered "high," while keeping the source at 0V. This opens the transistor's channel wide open, and electrons flow from source to drain. Some of these energetic electrons tunnel through the barrier into the floating gate, programming a zero.
To erase a cell, we disconnect the source, connect the control gate to 0V, and connect the drain to a high voltage. This causes the electrons to tunnel out into the floating gate, into the channel, and out through the drain. This removes the charge and erases the cell. This tunneling effect does not happen at normal energies, so the electrons cannot tunnel in or out of the floating gate until they get some not-so-subtle encouragement in the form of high voltages.
In the image below we can see how some electrons tunnel into the gate when programming, and how they are forced out during erasing.
Image 3: Programming a cell and erasing a cell.
Now that we know how cells work, we can shift our focus to focus on how memory is organized. There are two types of flash memory: NAND flash and NOR flash. These types are organized differently and have different uses.
NOR flash is organized in parallel cells, much like a NOR gate. NOR flash can be addressed bit by bit and is essentially a random access device. This makes it useful for storing firmware, substituting for ROMs.
NAND flash is organized in series. Instead of having single cells connected to the bit and source lines, cells are connected in series. This allows for higher capacity and lower prices in memory chips. The drawback of NAND flash is that it is a block oriented device: NAND flash cannot be read or written bit by bit, so it cannot directly substitute for a ROM. NAND flash DOES have a control line for each cell and needs two extra FETs to enable or disable a cell chain, but by having common bit and source lines, cells can be packed more compactly.
Image 4: Flash architectures.
If the cells are connected in series, how do we read each cell? This is just a matter of properly selecting the voltages sent to the control lines. To read a single cell of a chain of cells in series, we first consider that any voltage higher than VT2 will cause the cell to conduct, no matter its state. If we put this voltage on all cells except one, we have effectively bypassed these cells. By feeding the readout voltage to the remaining gate, we are determining the value of this cell. This allows us to read an arbitrary cell, or even to read all cells serially, but we would not be able to read them at the same time.
Now that we have a basic understanding of flash memory, we can move on to how Linux deals with it.