Thursday, January 28, 2021

Probing and Signal Integrity Fundamentals for the Hardware Hacker

by Andrew D. Zonenberg, Ph.D
Associate Principal Security Consultant

The latest new widget just showed up on your desk. You excitedly crack open the case, look around a bit, and find a signal that looks interesting. You fire up your oscilloscope, touch a probe to the signal, and... the widget won't boot! Or maybe it works fine, but you see garbage on the scope screen that looks nothing like a useful digital waveform.

It's a problem that's becoming all too familiar to hardware hackers. As technology advances, signals become faster, which makes them more sensitive and less tolerant to the sloppy wiring and probing techniques commonly used in reverse engineering. Even cheap SPI flash can run at 100+ MHz in modern designs.

This article will focus on practical problems and solutions for analyzing existing boards, without digging too deep into the more advanced electrical engineering theory needed for design of high speed logic. For a more in-depth treatment of the subject, High Speed Digital Design: A Handbook of Black Magic (1993) by Howard Johnson & Martin Graham is an excellent read, as well as its successor High Speed Signal Propagation: Advanced Black Magic (2003).

If your probing setups look like this, you're going to have a bad time reversing modern hardware!

At this level, the main thing to be aware of is that electrical signals move through wires at a finite speed, typically around 0.6-0.7x the speed of light (roughly six inches per nanosecond), and that changing cable characteristics (more specifically, impedance) causes an impact on the signal whose duration in time is proportional to their length.

Before we proceed any further I'd also like to introduce an abbreviation from the test equipment world that you may not be familiar with: DUT (Device Under Test). This is a fancy way of saying "the thing you're trying to probe".

Avoid Long Messes of Wires

In the photo above, a JTAG adapter was connected to the DUT with several feet of wire, without paying any attention to impedance or termination. The JTAG software was unable to detect the processor. When an oscilloscope was connected to the TCK signal on the DUT, the signal looked like this:

JTAG TCK signal with long wires

The rising edge of the clock signal climbs for a while, then dips, then continues climbing to its full value. These non-monotonic edges are a big problem! If the position of the dip happens to line up with the threshold of the device's input pin, it will see two rising edges instead of one, resulting in a bit of data being duplicated.

You might be tempted to reduce the clock frequency of the JTAG adapter in this case, but this won't help: all it will do is move the rising and falling edges further apart without changing their shape. Fast edges at 1 kHz are just as vulnerable as edges at many MHz.

Adding filtering to slow down the edge rate (at the cost of reducing the max operating speed), adding a ~33Ω series terminating resistor at the source end of the wire, or playing with ground geometry to match the impedance of the cable more precisely can all help. In most cases, however, the easiest solution is simply to shorten the wires.

No more spaghetti!

JTAG signal with shorter wires

If you look closely a small defect on the edge is still visible since the ribbon cable isn't impedance matched, but it's much smaller because the mismatched cable length is shorter. At this edge rate, this sort of defect shouldn't be a problem since it doesn't dip, it just stops rising momentarily. When dealing with sharper edges, you might want to shorten the cable even further. You should also...

Properly Secure Wires

This isn't strictly a signal integrity issue, but "my wires ripped off the DUT" will certainly lead to difficulty seeing the intended signal! Properly securing wires and probes is something overlooked far too often.

Don't rely on solder joints to provide mechanical support for a cable, which can be subject to bending and pulling forces as the DUT and cable move around your bench. If you're lucky the solder joint will fail and the probe will cleanly separate from the DUT. Worse yet, you might rip the copper pads/traces off the DUT or ruin your probe.

There are plenty of different methods of securing wires, so pick one that suits your specific scenario. I prefer to secure the wire first, then solder it, in order to keep it from flailing around while I'm soldering.
Hot glue: Cheap and easy, but hard to do neatly and difficult to rework if you get it somewhere you didn't intend. Requires rapid work to get things positioned right before it hardens and leaves annoying "strings" everywhere.
Kapton tape: Easily removable, but fairly low holding strength. You can buy it on big spools but I prefer using pre-cut dots to save lab time. Name-brand tapes generally have much better adhesive than cheap knockoffs.

Kapton tape securing a solder-in probe tip

Epoxy: Very strong, but almost impossible to remove once cured. Good choice for permanently attaching to something you plan to keep around the lab for an extended period of time.
UV cured wire tacking glue: My personal favorite (I'm a fan of Dymax #9-911, but there's many other options). It cleans up easily with alcohol in the uncured state, and solidifies in a minute or two under a cheap gel nail polish curing lamp. Most of these glues have intermediate holding strength: the wire is unlikely to come free by accident, but careful prying with a knife blade will cleanly separate it from the DUT without damage.
Wires secured with UV glue

Probe holders: There's a wide range of these available, ranging from DIY 3D printed bipods to $500+ three-axis micrometer jigs. More temporary in nature than any kind of adhesive, but useful for any time you want to look at more than a handful of signals while keeping your hands free. These get the probe tip right up against the board without any additional wire, which is great for probing faster signals.

Probe held in a bipod positioner

Avoid Long Probe Grounds

Yes,I'm talking about that big "tail" on your scope probe with an alligator clip at the end. Forget about using it on anything fast. It's very inductive, which when combined with the capacitance of a typical passive probe creates resonances.

10 MHz clock seen through a 10MΩ probe with a long ground wire
In the 10 MHz clock seen above, the ringing isn't enough to completely destroy the shape of the signal, but it doesn't take much imagination to see that with anything a little bit faster, the signal would be completely unreadable.
In the next few examples, we'll be looking at a 1.25 Gbps Ethernet signal. The "idle" sequence between packets contains a nice mix of fast 0-1-0-1 transitions and runs of several 0 or 1 bits, providing a nice idea of what a data bit at different rates might look like through a given probing setup. 

Test signal with no probe

In this screenshot we see three different signals:

  • Blue: The signal as seen through the probe (flatlined, since the probe isn't on the board yet).
  • Pink: The signal internal to the DUT
  • Yellow: A snapshot of the pink waveform frozen in time. Right now they're identical, but if the probe alters behavior of the DUT you'll see them deviate.

To start, let's use a standard 10MΩ passive scope probe (a Teledyne LeCroy PP022) and an alligator clip ground.

Probing the easy, obvious way.

And here's what the scope sees

The signal is completely unrecognizable through the probe! In addition to causing ringing on edges, the inductance of the long ground lead destroys high frequency performance.

A spring ground is a bit trickier to use since you have to find a good ground point fairly close to the signal you're probing on the DUT, but gives a much nicer looking view of the signal.

Using a spring ground

Waveform seen with the spring ground

That's a lot better looking: the edges are rounded off and not as square looking as they should be, but that's an inherent limitation of this probe (500 MHz bandwidth). Individual data bits are clearly readable, so all is well... right?

Wrong. Take a look at the signal on the DUT (pink waveform)! There's huge dips in it. This might well be enough to corrupt data and render the DUT nonfunctional. This brings us to our next topic...

Beware of Probe Loading

You might be inclined to look at the "10MΩ" marking on a scope probe and assume that it imposes infinitesimal load on the DUT. That may be true at DC, but not at frequencies typical of modern electronics.
The problem is that typical 10MΩ scope probes also have on the order of 10 pF of capacitance. When a high frequency signal is applied to a capacitor it acts somewhat like a resistor, with an effective resistance of 1/(2*pi*f*C) for frequency of f Hz and capacitance of C farads. So the actual load the probe imposes is around 159Ω at 100 MHz and 31Ω at 500 MHz - a far cry from the 10MΩ seen by a DC signal! Since this loading is frequency dependent it will distort the signal as well as weakening it, potentially rendering it unreadable to the intended destination on the DUT.

Unfortunately, there's no trivial way to work around this issue using a standard passive scope probe, but there are several different types of probe designed for higher speed use which can help.

Active probes, which contain an amplifier in the probe head, can have much lower capacitive loading. A typical 1.5 GHz active probe might have an input capacitance of 0.9 pF, making it much less invasive. The price you pay for this is very expensive hardware - active probe price tags start in the low four figures (USD) and go up from there.

For looking at typical digital signals, a good low-cost option is the transmission line probe. This has a low-value resistor, typically 450Ω, at the tip and connects via coaxial cable to an oscilloscope input terminated with a 50Ω resistor to ground, for a total of 500Ω of loading as seen by the DUT. (Although this may present a problem when looking at signals with pull-up resistors, these are typically low enough speeds that a conventional 10MΩ probe can be used instead.)

Probing our test setup using a transmission line probe (Pico TA061)

Waveform seen through the transmission line probe

The benefit of a transmission line probe is that the input load is, in theory, purely resistive. Although there's always trace amounts of "parasitic" capacitance due to the physics of having the ground a finite distance away from the input, it's normally a fraction of a picofarad, which results in a high speed signal on the DUT being loaded much less heavily. As if that wasn't good enough this lower capacitance means that a high-inductance ground, like the standard alligator clip lead, will be much less likely to resonate - allowing this poor quality (but convenient) grounding method to be used with much faster signals.
In this screenshot the signal is clearly intelligible despite the long ground, and while the waveform as seen by the DUT has measurable loading it's far less severe than with the 10MΩ probe.

Waveform seen through transmission line probe with spring ground

Unsurprisingly, switching the transmission line probe to a spring ground improves the signal quality further - however, some degradation of the signal on the DUT is also visible. This is caused by the ~2 pF of parasitic capacitance of the probe. (The higher inductance ground lead concealed this loading in the previous test.)

If you don't want to spend $329 for a commercially made entry level transmission line probe (or $1K+ for a higher end transmission line probe like the Teledyne LeCroy PP066 or PicoConnect 900 series), you may wish to consider building your own. Simply solder a 450Ω resistor to the signal on the DUT you wish to probe, then solder the center terminal of a piece of 50Ω coax to the other end of the resistor and solder the ground braid to any convenient ground point nearby on the DUT. Attach the other end of the coax to your scope and you're in business.
Squeezing maximum performance out of this DIY probe design requires careful engineering, but building something that outperforms a standard 10MΩ passive probe for instrumenting fast digital logic isn't difficult. If you don't want to spend the time tweaking it, you might also want to consider...

Open Hardware High-Speed Probes

For the past year or two I've been working on two open-hardware transmission line probes, the AKL-PT1 (handheld, 6 GHz bandwidth) and AKL-PT2 (solder-in, 4 GHz bandwidth).
These probes target the OSHPark 4-layer and flex board stackups respectively. They're still being fine tuned but already deliver very nice results (outperforming every <$2K probe I've tested them against) so feel free to download the KiCAD files and try building one!
There will probably also be a group buy of assembled units with nice plastic enclosures and proper impedance control at some point in the near future once I'm finally satisfied with their performance.
AKL-PT1 in use

AKL-PT2 soldered to a board 
Same Ethernet test signal seen through the AKL-PT1
With this knowledge, you should be able to acquire more accurate and useful data from your embedded targets.

Thursday, January 7, 2021

TAPing the Stack for Fun and Profit: Shelling Embedded Linux Devices via JTAG

By Ethan Shackelford

While it may not come as a surprise to those who have worked with embedded devices, it is not uncommon to find the JTAG interface on embedded devices still enabled, even in production. Often cited as providing “ultimate control” over a device, JTAG debugging usually provides the user with full read/write privileges over device memory (and by extension over MMIO) as well as the ability to read and write control registers. 

On a simpler embedded device (for example, a "smart" lock), leveraging open JTAG can be fairly straightforward: dump the bare metal firmware via JTAG, reverse engineer it to determine the details of its operation (such as the function which performs validation on the entered PIN code), and either use the JTAG interface to re-write the firmware in flash, or flip some bits in memory (in this example, to trick the PIN code check logic into accepting an invalid PIN code). 

However, embedded Linux platforms can complicate this process substantially. First, the firmware is not bare-metal, but instead a complex system including a multi-stage bootloader, the Linux kernel image, and various filesystems. Second, storage devices are more likely to be managed and mounted by the kernel, rather than memory-mapped, making access to peripherals over JTAG substantially more complicated than on bare metal systems. Third, MCUs used for embedded Linux tend to have a more robust MMU and memory management scheme, which can make addresses reported by the MMU during JTAG memory access more difficult to correlate to locations in physical memory -- a correlation that is sometimes necessary thanks to the second hurdle created by a more robust MMU, where it also restricts memory access depending on the execution context, which means that the MMU must either be disabled entirely, or configured to suit the task at hand. 

Thus, while open JTAG does usually grant ultimate control over a device, the operations performed are sufficiently low-level as to make actually manipulating a Linux system in this way a difficult and somewhat arcane task. However, operating in a Linux environment does have its perks: rather than the more complex task of manipulating device operation directly over JTAG in a way specific to the particular device being tested as is the case for bare-metal operating systems, leveraging JTAG on any Linux-based device typically revolves around a uniform goal: access to a root shell. This uniformity means that a similar group of techniques can be used to gain access in any case where a Linux-based device has an enabled JTAG interface.

Despite the obvious and widespread nature of this issue, there appears to be little to no existing work published on this topic. To help fill this void, this article is intended to detail the process of using access to a JTAG interface to move to a root shell on a Linux-based device, as well as the common issues and pitfalls which may be encountered therein.


Interacting with the TAP controller

While this document will not go into great detail on the inner workings of JTAG and the associated tooling, the following is a short crash course on using OpenOCD and a JTAG hardware adapter to interact with a device’s TAP controller.

Interacting with a device via JTAG requires some means of driving each its pins. This can be accomplished with specialized hardware, such as the SEGGER J-Link series of adapters (which I will use for the purposes of this article), or just as easily with a more generic device like a Bus Blaster or FTDI dongle, such as an FT2232. The experience with a device will vary depending on how well it is supported by OpenOCD -- the Bus Blaster and SEGGER J-Link are both well supported. 

 Determining JTAG pinout on a given board is outside of the scope of this document, but a wealth of information for doing so exists online. Once the pinout has been determined, the device under test and the chosen JTAG adapter should be connected appropriately. Pictured below is the example device and a J-Link adapter, connected based on the determined pinout of the board’s JTAG header.


After connecting to the device, OpenOCD can be used to interact with the TAP controller. In order to do so, OpenOCD requires information on the CPU to which you're connected and the adapter used for the connection. A standard installation of OpenOCD will include configuration files for a number of common adapters and targets. It supports many adapter/target combinations out of the box, and on a standard installation the appropriate configs can be found in the target, board, and adapter subdirectories of /usr/local/share/openocd/scripts. 

In this example, we're working with an NXP iMX.28 CPU, and the SEGGER J-Link, so the call to OpenOCD looks like the following:


To verify a valid connection, confirm that the reported IDCODE (here 0x079264f3) matches the expected value for the targeted processor; this information can be found in the datasheet for the processor. 

For simple, manual interaction, OpenOCD exposes a telnet listener on port 4444. OpenOCD can also be loaded with TCL script files at launch, by passing additional “-f” flags pointing to the desired scripts. 

Now that we can talk to the TAP controller, we can move on to the various techniques for leveraging the JTAG interface.



Depending on the processor, the actual implementation of various functions available over JTAG may differ. In general, the same set of primitives will be available: read/write to flash and other memory mapped IO, read/write to various CPU-specific control registers, and read/write to RAM. If you're familiar with more traditional exploitation techniques, these probably sound a lot like read-where/write-where memory-corruption-based exploitation primitives; this is a useful way of thinking of them. 

There are a few constraints to keep in mind while attempting to gain code execution on a Linux device over JTAG:
  • While the JTAG interface has full control of the processor, this doesn’t mean that extra steps won’t be required to make use of it. For example, the debug interface write to any area of RAM, but it will be doing so in the same context as whatever program was running when the CPU was halted. In the context of a Linux system, this means the same rules that apply to programs apply to any memory access by the debug interface – access an area of memory outside the virtual memory space of the currently executing process, and the MMU will reject the memory access and generate a DATA ABORT exception. 
  • Not all execution contexts are created equal. Anyone taking their first steps toward exploiting Linux kernel modules will know that code executed from the context of the kernel (or “Supervisor” from the perspective of the CPU) is generally incompatible with code and exploitation techniques written for userland. While it is possible to tailor shellcode to either context, the context should be kept in mind when setting things up. 
  • As we will see shortly, making our way to a shell from these primitives involves overwriting some section of memory. Of course, if the code or data we overwrite with shellcode is critical to system operation in some way, the system will no longer function as expected, so take care in selecting an area of memory that can be overwritten without interfering with the system as a whole.
For these examples, the following conditions were chosen for exploitation: 
  • We will be exploiting the JTAG access from userspace, rather than from kernel code. There is no real reason for this, other than the fact that it's a bit simpler than acting from kernelspace, and it so happens that any shell on the device under test will be a root shell, so we don’t gain any additional privileges by going the kernelspace route.
  • The device under test provides an Ethernet interface for communication with the outside world. This will be our target for gaining a shell by opening a reverse shell over an unused TCP port. If the device did not have this interface and instead had, say, a UART console, the timing, memory areas, and shellcode chosen would differ somewhat. However, the overall procedure in this article would still apply.
The first step is simply to generate some valid shellcode to load into memory. To generate shellcode for performing our chosen operation, a reverse TCP shell, we use MSFvenom, specifying the target architecture/platform (armv5le, linux). We want the raw byte output, as we’ll be injecting it directly into memory.


As illustrated above, the size of the shellcode in this case is 172 bytes. Not huge, but it could certainly pose a problem depending on how the MMU is configured. In fact, in the case of our example device, this tends to exceed the MMU-allowed writable length of memory under $pc, and we will need to address it as we perform some of the techniques outlined below.

Smashing with Precision

Although the famous Phrack article is nearly a quarter-century old at time of writing, the old-school stack smashing of days gone by is alive and well in the embedded world. It is not an entirely uncommon, depending on the architecture family/version and degree of programmer negligence, to find devices in the embedded world which either do not have a “no execute” flag for memory access, or have the flag disabled if it is available. If this is the case, gaining a shell is very straightforward: halt the CPU, overwrite the stack with shellcode, and jump to $sp. In most cases, the stack will have enough space allocated to avoid generating a segmentation fault on write, and is always writable by nature, enabling us to sidestep any MMU shenanigans that might be required for other techniques during exploitation. 
This technique requires little to no knowledge of the software running on the device, and generally will not crash or permanently alter the operation of the device. It still might crash, if you get unlucky with the function whose stack is being overwritten, and so isn’t suitable for testing environments where any degree of device malfunction is unacceptable.


A number of factors may exclude this technique as a possibility, such as a no-execute bit or stack constraints. It may be possible to work around these factors via the JTAG interface, but in the case that a workaround fails, other techniques are available.


Firing From the Hip

In general, device exploitation can be aided by access to a copy of the firmware. If firmware is not available, it's possible to feel your way around the system semi-blind, halting and inspecting configuration registers to determine when you’ve landed in a suitable place. This technique will also routinely crash the device, unless you get incredibly lucky on your first attempt, so this is not a technique suited to an environment where a crashed device is not a recoverable scenario.

The MMU will need to be addressed to allow manipulation of memory, so that shellcode can be written and execution resumed without generating a fault. To do so, we can either reconfigure the MMU to allow reads/writes to the areas to which we want to write, or just disable the MMU entirely. The former option is more complicated to set up, but the latter requires extra work since without the MMU we’ll need to translate virtual addresses to physical addresses ourselves. However, disabling the MMU is also a more universal option, easily applied to a variety of CPUs and architectures.

Here we opt for disabling the MMU, writing shellcode under the memory pointed to by the program counter, then re-enabling the MMU to allow execution to continue without error. There are a few techniques for correlating MMU-enabled (virtual) addresses with the MMU-disabled (physical) addresses; in this case, dumping the full system RAM via JTAG with the MMU disabled, then using a simple homemade tool to match the bytes under the program counter to an offset into the RAM dump. 

This tool can be used with the output of OpenOCD’s dump_image command to find where, in the physical address space, a given virtual address is pointing. While the dumped RAM image may not be identical between boots, the locations of loaded binaries in memory will generally stay the same, because the device will tend to run the same programs in the same order every time.


 The image above illustrates the process of using OpenOCD in conjunction with the tool:
  1. The CPU is halted here, and found to be in the “User” execution context. If it had been halted under any other context, most likely “Supervisor,” the CPU would be resumed, and the process repeated, until the CPU is halted in the correct execution context. 1024 bytes starting at the program counter are then dumped to a file.
  2. That resulting file is fed into the tool, and found within the dumped RAM image. The tool outputs the address at which a matching sequence of 1024 bytes is found (the physical address).
  3. The MMU is disabled here, and the shellcode is written to the physical address to which the virtual address in the program counter is mapped. The process of disabling the MMU is CPU-specific; information on how to do so can be found in the appropriate reference manual. Here it is accomplished by clearing a specific bit in a specific control register.
  4. The MMU is re-enabled and the memory under the program counter is read to confirm the shellcode has been written. The bytes read match the bytes of the shellcode, confirming we have written to the correct physical address.
  5. The CPU is resumed, with execution continuing from the program counter, our shellcode now occupying the memory to which it points. The netcat listener receives a connection, and a bash shell is available to the attacker. In this case, the shell runs with root privleges.
So, easy right? Not exactly. 

The far more likely outcome during the above process is that the userspace code we randomly halted on will either be some crucial library function, causing a crash at some subsequent step in execution of the shellcode, resulting in a failure to gain a shell and probably breakage of most other device functionality (think overwriting malloc with shellcode), or some piece of code whose absence prevents successful shell access. In practice, actually getting through this process with a shell at the end required around 20-30 attempts on average, with many failed attempts requiring a device reset to un-break critical library code. 

This is fine, if tedious, under circumstances where a device can be reset and taken down arbitrarily. However, in contexts where a device cannot be taken offline or reset, this approach is obviously not viable.


Know Thine Enemy

In cases where device firmware is available, or where the exact version of libraries or other software in use by the system are known or possessed, we can take a more targeted approach. The ability to identify the location in physical memory of a binary or library known to be called during device operation enables us to overwrite a function from within that binary or library with shellcode. Upon execution of that particular function as part of the normal operation of the device, the shellcode will run in place of the function normally at that offset into the binary. When we can choose a function to overwrite whose abscence will not cause any anacceptable side effects for the device, we can avoid device crashes or other interruptions.

In the case of the example device, the firmware is available, and we can use one of a variety of binary analysis tools to determine an appropriate function to overwrite. IDA, Binary Ninja, radare, and even the humble objdump are just fine for this purpose. For this example we'll use Binary Ninja. 

In looking for an appropriate function, we should seek the following conditions:
  • The binary or library should be loaded at startup, and continue running during device operation. This ensures that it will be present in RAM when memory is dumped, and that its position in RAM will be the same between boots. This rules out something like a cgi-bin called in response to an HTTP request, which would otherwise be a good candidate.
  • The function should either have some external attacker-accessible trigger, or should be a function that repeats reliably. If the function only runs once, during system boot, the shellcode will never actually execute if it's loaded after the function has been executed.
  • The function should not be system-critical, especially in the case of standard libraries. Its fairly trivial to track down an appropriate version of libc or libpthread, for example, and simply overwrite something like pthread_mutex_unlock, with a pretty strong guarantee that the function will run at some point, triggering the shellcode. However, the next time a function needs to unlock a mytex, the system will come crashing down, requiring a reset.
  • While not required, it can be useful to choose some binary or library whose source code is publicly available, as the code can come in handy to work out the most appropriate function to overwrite and save some time otherwise spent reverse engineering.
After some digging around, we identified a good candidate for shellcode injection in a vsftpd server running on the device. Exclusively used for OTA firmware updates, a lapse in functionality in this server will not interfere with the operation of the device. In a real-world scenario where the ability to initiate an OTA update would eventually be required again, after acquiring root shell access the vsftpd server can be restarted and reloaded into memory uncorrupted by shellcode.


Some minimal reverse engineering and cross-referencing against the vsftpd source turns up a function, vsf_sysutil_retval_is_error, which looks like a good choice. It appears to be vsftpd's error checking function, meaning it will likely be called during most operations. This is supported by a fairly large number of cross references in a wide range of functions. One drawback is that the function itself is only eight bytes long, and overwriting it will clobber the function positioned under it in memory. This means that the vsftpd server is toast after our exploit is fired, but as mentioned before, we can simply restart the vsftpd server from our root shell if desired. 
Using the same tool as in the previous techniques, we find the physical address in RAM of the function we'd like to overwrite. This time, rather than searching for the bytes dumped from under $pc, we look for 0x100 bytes or so from the vsftpd binary, starting at the beginning of the target function. This can be done with dd, or just by copying-and-pasting if you're using Binary Ninja or similarly equipped software.


Moving back to the JTAG connection, we can now write to the acquired physical address (shown in the preceeding image), overwriting the target function with shellcode. To trigger its execution, we'll use an FTP client to connect to the target device, which at some point calls the vsf_sysutil_retval_is_error function as expected. 


Further Reading

  • JTAG Explained (finally!) - A fantastic introduction to JTAG from a security perspective, including some information on performing a similar attack on a device with an exposed UART console.
  • Pinout Reference - A useful collection of various standard JTAG header pinouts, useful for identifying mystery pinouts on boards under test.
  • Smashing The Stack For Fun And Profit - A must read for the aspiring hardware hacker, many of the points discussed within apply equally when working with a device over a JTAG connection.