Pulling Bits From ROM Silicon Die Images: Unknown Architecture

Ryan Cornateanu
27 min readMar 21, 2021


Table of Contents


My journey into Integrated Circuit Reverse Engineering (ICRE) has only just begun but I am completely consumed with passion for this field. Besides the computer/electrical engineering aspect to ICRE, there is a lot of physics and chemistry knowledge that is needed. For a while, the chemistry component scared me, as I had little to no knowledge of chemistry. Not to mention, working with the products necessary to decapsulate and delayer chips is quit dangerous.

Believe it or not, I prepared for about 2 years before actually investing in a laboratory. I didn’t want to do my first experiment until I had all the safety equipment in place in order to take the proper precautions. It is common knowledge that the first rule of chemistry is when, not if things go wrong, you have to be prepared to know what to do next. However, being my obsessed self, when I have my eyes set on a goal, there is nothing that can stop me from pursuing it.


First things first, I had to look into a bunch of really expensive items and solvents to buy. Here is a list of equipment and supplies I purchased.

Why did I need a metallurgical microscope versus something like a stereo or compound microscope? Since most microscopes have a bottom-up light to reflect against the XY stage, this wouldn’t exactly work for IC’s as they are not double sided. The actual layers of die would need a light to properly reflect from the top-down. Metallurgical microscopes make use of something called EPI illumination, which is a unique type of illuminator that is also referred to as reflected light or epifluorescence illumination. Apart from illuminating the IC object/sample, the microscope objective also serves to collect light that is reflected from the sample surface.

The rest is pretty self explanatory, in order to properly remove the IC from the epoxy packaging, acid at room temperature will not be enough to melt away the epoxy. Some of the other material purchases on that list are pretty standard laboratory stuff that you would need in order to handle the chemicals. As for the actual chemical products, I’m going to explain what I use in terms of my acids, bases, and oxidizers as I proceed with experimentation.

Deciding on a Lab Sample

Ironically, when I first decapsulated the CH340G from the Arduino Nano v3 board, I actually had no idea I would be stumbling into an entire section of ROM before beginning the delayering. Normally, when picking out a ROM project, you should have a fairly good understanding of your target; this includes familiarization with the architecture and processor from reading the datasheets. Fortunately, this was not the case for me, and you’ll find out why later on in the paper.

The reason I went with desoldering integrated circuits from the Arduino board in the first place was because it was the first dev board I ever used in my journey into embedded systems security. Consequently, I figured it would be super nostalgic to throw a couple of chips that I loved working with into fuming hot sulfuric acid. To be honest, I was very hopeful that the ATmega328P that I did my first experiment on would prove to have some ROM but after many delayering attempts with hydrofluoric acid, all you could see is the EEPROM, SRAM, and flash.

A delayered Atmega328P

Getting down to the polysilicon layer of this chip (SiO₂ / Silicon Dioxide), did indeed start to show a lot of interesting sections of the chip but none of which we could do a firmware extraction with, with the tools I had present.

Side Note: If you would like to see a high definition of this image, please head over to this link at my personal page on siliconpr0n.

Flash vs ROM Microscopy

You might be asking yourself, why can’t you also just read data off the flash memory segments using a metallurgical microscope like you would ROM? First, we should discuss the difference. Mask ROM (MROM) contains firmware code that is burned into the silicon of the chip during the design phase of the semiconductor manufacturing process. MROM are produced by arranging transistors before the photolithography process begins. Under a microscope, they can look very different from one another:

TI TMS5200NL ROM vs CBM 65CE02 ROM from SiliconPr0n

These are just two separate examples of what an MROM transistor will look like on a substrate level. These transistors can use either n-type material, which contains a higher concentration of electrons, and is doped with a Phosphorus atom, or p-type material which is doped with a Boron atom, and contains a lower concentration of electrons.

On the other hand, Flash memory is not so simple. 1 transistor != 1 bit. The reason why a scanning electron microscope is needed to pull bits from something like an EEPROM type memory is because flash memory makes use of a “dam” system that is able to store remaining electrons from electrical currents regardless of whether there is electricity running through it or not, meaning that this is considered non-volatile memory. There are four principle parts to a flash transistor: a source, a drain, two gates called the floating gate and control gate, and an insulating material dividing the other three parts. They are in an inverted T-shape, with the lower partial of the transistor being the source and the drain, and the upper partial being the gates, with the control gate above the floating gate part of the transistor. The gates are encircled by oxide layers through which current normally cannot pass through.

Transistor Mock-Up in NAND Flash

Negatively charged electrons are in a ready state at the source (the originating point of the electrical current) and the drain (the draining point of the electrical current), this is due to the type of silicon used to construct these fragments of transistor material.

However, the drain cannot flow as anticipated because of the electron deficient insulating material between the source and the drain. When you force a positive voltage to the electrical contacts at the drain and the gates, the negatively charged electrons are attracted to the positively charged electrons and gets transported over to the drain. A few electrons will get pulled into the floating gate through the oxide layers through a process known as quantum tunneling. Electrons that have gone into the floating gate to bury there are unfit to break out from the oxide layers, and will remain in the floating gate indefinitely. Flash transistors with stored electrons are parallel to RAM transistors with current flowing through them. To separate the electrons, a negative voltage is registered to the electrical contact above the floating gate, which causes electrons to be pushed away from the floating gate.

Flash memory transistor cells

This is what a flash transistor will look like at a poly-silicon level under a metallurgical microscope. You will not be able to see any charges passed through the floating gate of the transistor because it cannot scan a focused electron beam over a surface to create an image, like a scanning electron microscope (SEM) would. When looking through images from an SEM, you would be able to see that a beam interacted with the integrated circuits flash surface, producing numerous signals that can be used to acquire information about the surface topography and composition.

How Bits Are Read

The reason we are able to see individual bits (‘1’s & ‘0’s) under a metallurgical microscope is because the bits are physically coded into the die. As shown in Ken Shirriff’s paper on Extracting ROM Constants, bits are programmed into the MROM by changing the silicon doping pattern, creating transistors or leaving insulating regions. In Ken’s example, if the presence of a transistor in a row exists, then we can assume that it is a 1 bit transistor. Generally, a row in a NOR MROM will contain two stacked transistors top-to-bottom per column as seen below.

ROM Image from TMS320C52

ROMs typically use multiplexers to select bits per column and row. If using a 16-bit MUX, there will be 4 select lines that can be activated. For the ROM I’ll be showing, each select line can turn transistors to a HIGH voltage state if activated. If a transistor is missing from a column and row position, then the output line will remain in a LOW voltage state.

Side Note: In our case, for the CH340G, the MROM is going to look completely different from all of these images seen above.

Getting Our Sample Ready

In the case of the Arduino Nano, the CH340G is always located at the bottom of the PCB. I took my desoldering heat gun and applied around 200°C of heat right above the target IC chip pins. This will take the solder from the joints and melt it, allowing you to safely remove the chip from the board.

CH340 soldered onto the Arduino Nano (no chip markings)

Once we have our sample ready, we can begin the next phase of the project, which is decapsulation. This is the complete removal of the black epoxy packaging that you see surrounding the silicon die inside.

Decapsulation Reactions

Now is the time to get extra cautious because it is extremely dangerous working with corrosive acids. We are going to take a glass pipette (as glass does not react with H₂SO₄) and take around 20mL of 98% concentrated sulfuric acid from the container and place it into a 100ml beaker. We can drop our sample into the acid using some tweezers then place the beaker on the hotplate.

Decapsulating inside of a fume hood

I recommend a temperature of at least 170°C, and not 150°C as shown above because the hotplate is never directly accurate. The reason for the extreme heat is because at room temperature H₂SO₄ oxidizes epoxy very slowly. By applying heat, we can speed up this reaction. In the right-most picture, you’ll notice the liquid starts to turn a yellowish color, and that is an excellent sign. It means our reaction is working and the epoxy is starting to melt away.

If you’re wondering why I have a glass lid on top of the beaker, it is for two reasons:

  1. It will help contain the fumes created from the heated sulfuric acid. At this point, you should already have your fume extractor fan running inside of the fume hood.
  2. It could potentially recirculate some of the sulfuric dioxide (SO₂) fumes, which in turn will keep the acid concentration high. If the acid concentration drops too low, the probability of corrosion increases. I’m not 100% sure on this though, so feel free to ping me if I’m incorrect about this. I know this will work well with nitric acid (HNO₃) as the nitrogen dioxide (NO₂) fumes can recirculate back to HNO₃ in the presence of water.

The big question now is, how long do we keep this going for? It depends on the thickness of the chip; in this particular case our datasheet shows that this is a SOP-16 chip package with a thickness of around 1.50mm. Around this thickness and temperature, the whole process should take around an hour.

Chip in process of acid bath

Another good sign to stop is when the entire beaker is consumed with what looks like black sludge, it probably means all the epoxy has been dissolved from the die. Now time to remove it from the hotplate and let it cool off.

Side Note: Once you remove the glass top, you’ll start to see a lot of SO₂ fumes, so be sure to have your fume doors closed and let the vacuum suck all that noxious vapor up. A good example to see how this reaction works is to do an equation with polyethylene ((C₂H₄)ₙ) which is commonly used in epoxy plastics. As sulfuric acid heats up the epoxy, the H₂SO₄ breaks down into SO₂, CO₂, and H₂O. Here is the balanced equation: 6H₂SO₄ + (C₂H₄)ₙ → 6SO₂ + 2CO₂ + 8H₂O. The boiling point of this acid is around 337°C, which is how azeotropic sulfuric acid is synthesized in the first place. By taking sulfur (S), oxygen (O), and water (H₂O), and burning the sulfur to create sulfur dioxide (SO₂); S + O₂ → SO₂, sulfur dioxide is then oxidized to sulfur trioxide (SO₃) using oxygen in the presence of a vanadium oxide (V₂O₅) catalyst, 2SO₂ + O₂ + V₂O₅ ⇌ 2SO₃. The water comes into place to hydrate the sulfur trioxide into sulfuric acid, SO₃ + H₂O → H₂SO₄. There are other methods that have been used, like adding electrolyzed solutions such as copper (II) sulfate (CuSO₄) or hydrobromic acid (HBr) to react with sulfur.

With that being said, we never want to get H₂SO₄ to its boiling point, as the acid will quickly decompose and turn completely gaseous.

At this point, I would pour the acid into another empty beaker and check the bottom of the beaker for any traces of silicon die. Sometimes, you really have to search as these dies can be really small.

Extracted silicon die from epoxy packaging

Once found, take the silicon die and place it into a smaller beaker filled with acetone (C₃H₆O). After the chip is submerged into this liquid, we place the beaker into an ultrasonic bath to clean off any burnt epoxy particles from the silicon. Why acetone? Acetone is a strong industrial solvent that is going to be gentle on the metal surfaces of the die.

Examining the First Sample

Now that our chip is rid of residual burnt epoxy, it is time for our microscope imaging so we can truly see what lives inside of the die.

CH340G imaged using 5x objective lens

When I first looked at this chip I did not see anything that would be considered ROM; knowing what I know now, I feel kind of silly. After further research, I was able to make some assumptions that the left handed area contained some sort of EEPROM or RAM, or was just some type of volatile memory holder. The top right area seemed to be the MROM. So let’s dive a bit further.

If you want to see a higher resolution here, click here.

Zooming into the logic gates using a 50x objective lens, we can start to make out some of what is going on here. The first interesting bit was noticing that there were 14 multiplexers on this ROM, meaning 14 column groups. Each of them is a 16:1 bit MUX. Meaning it can separate 16 different data paths into a single path.

14 column groups means that we are dealing with a 14-bit architecture, which is odd. Normally you would see microprocessors in the 4-bit, 8-bit, 16-bit, or even 32-bit range. This will make things a lot more complicated later on after extracting the bits from these images as it is probably a custom architecture.

Other notable observations on the top layers of this chip are the address lines. The vertical column shows 10 metal lines which end up translating to 6 address bits, and 6 metal lines on the horizontal row which end up translating to 4 address bits. This will be explained later when we delayer the chip but this essentially means that the 4 address bits give you 2⁴ = 16 for the multiplexers explained earlier. Now, the 6 address lines will be used to select one of the 64 rows of ROM space, which gives 16 bits x 14 columns horizontally, and that is why it needs 10 address bits vertically in total.

The next part of this experiment is to expose the substrate layer so we can see the individual bits through the microscope. This is a pretty wild concept to be able to make out ‘1’s and ‘0’s hardcoded into the chip itself, and you’ll soon see why there is such a big community around extracting firmware from silicon.

Delayering Reactions

There are many ways to delayer a chip but we will only go over a couple of them. The first thing we will need is the PTFE (Teflon) beakers I mentioned earlier in this paper. Neither hydrochloric acid (HCl) nor hydrofluoric acid (HF) react with this type of material and this material can be heated nicely on a hot plate. Well, to be more specific, with HCl, we can use glass beakers but with HF we cannot.

Side Note: There are some similarities when it comes to both of these inorganic compounds. Both HCl and HF are ionic compounds, which implies that they would both dissociate completely in an ionic solvent like water. HCl does this, and is therefore classified as a strong acid, while HF does not. This is because the strength of fluorine and chlorine differ. Fluorine is strongly bonded to hydrogen so less of it dissociates. The fluoride ion is so electronegative that not even the powerful ionic effect of water can completely pull it apart from its hydrogen atom; essentially this is what makes HF so dangerous… it will react with anything it can get its electrons on, especially the calcium ions in your bones. A certain percentage of the HF molecules will not dissociate. HF is therefore regarded as a weak acid. In our use case, it’s important to note that they both react very violently when introduced to metals, but the uses can be quite different when it comes to delayering.

So how do we know which acid to use? There are two types of metal typically used in the semiconductor fabrication process: aluminum (Al) alloy 6061 and/or copper (Cu) . For chips similar to this, you will usually see Al but in rare cases you may see Cu. This will be because it is desirable for the metals interconnectivity as it has lower resistance.

If we were dealing with Cu we would use HCl, however HF does not etch Cu, and in fact will cause a lot of corrosion caused by atmospheric oxygen. Any sort of oxidation agent will not serve you well when it comes to delayering Cu with HF. Bear in mind, HCl alone will not etch Cu, it needs an oxidizing agent like hydrogen peroxide (H₂O₂) to allow the acid to eat away at the Cu (the reducing agent) by rising the pKa of the acid solution. The pkA is a measurement of the acidity strength by its acid dissociation constant. These two reactions together at a 1:1 ratio will synthesize hypochlorous acid (HOCl) and water (H₂O). Once the Cu from the die is introduced, it will react with the HOCl to make copper (II) chloride, which will end up being the actual etchant. This means it will delayer the Cu area of die at room temperature to a greenish colored cupric chloride (CuCl₂) solution.

H₂O₂ (aq.) + HCl  (aq.) → H₂O + HOCl (aq.)
2HOCl + Cu → Cu(HOCl)₂

Side Note: Be extremely careful, as unlike most acids at room temperature, HCl solution will heat up pretty quickly on its own so it will most likely release hydrogen chloride gas. This has to be done in a fume chamber.

In our case for the CH340, we only have a very thin layer of Al to etch through, and the silicon dioxide (SiO₂) is pretty much non-existent. This is why we are going to use HF for our delayering. Wet HF will attack the Al interconnects and bond pads at a very fast rate past 40°C, but you can also etch at room temperate with a very low concentration using Whink. This rust stain remover contains a concentration of 3% HF, but don’t be fooled because it can still be extraordinarily fatal. My recommendation would be to let the chip sit in at room temperature inside a Teflon beaker for around 15 minute intervals, depending on the chip under test. This will not only etch the Al but the SiO₂ as well.

SiO₂ + 4HF → SiF₄ + 2H₂O

This is a pretty slow reaction when using low concentrations and the absence of heat, but it is the safer method as you won’t have to deal with fumes. The end result should look something like this:

Delayered CH340

We have now successfully etched our way down to the substrate, or transistor layer, of the chip. You can find a higher resolution image at siliconpr0n. Zooming into the top right corner, we find our prized possession of MROM.

Top Right Corner: MROM of the CH340

As you can see, I probably etched a bit too much, as some of the transistors seemed to be fading away. Alas, this is not going to be a major problem when extracting the bits but some automated tools may go a bit haywire. Therefore, it will be more work but we can always manually override any errors we might see.

Automate Extraction of ROM Bits

Enter rompar, an interactive tool used for extracting binary data out of MROM images using computer vision. It can be a bit intimidating at first, and there is a bit of a learning curve, but after a few runs with it, it is not so bad. The first thing we are going to want to do is prep our image, either using Gimp or any photo editing tool of your choice. The point is, we want to isolate and expand the MROM region of the image, crop it out, and sharpen it.

Before running the tool we need to know how much ROM we are working with. Looking at the picture, we can see there are 14 column groups that contain 16 bits per row, so that means 224 bits in each row. The rows will be the next important part, and it seems like we can see 64 rows here. In result, our ROM size we are working with is 1.7Kb.

When we run rompar, it’s going to expect 3 arguments; the file image, number of columns, and numbers of rows.

➜ python3 rompar.py image1-50x-ROM.jpg 16 1
Changing edit mode to GRID
Changing edit mode to GRID
Image is 11694x4318; 3 channels
process_image time 0.18801593780517578
read_data: computing
grid line redraw time: 6.4373016357421875e-06
grid circle redraw time: 1.1920928955078125e-05
render_image time: 0.22574210166931152

You might be asking yourself why the 16x1 configuration? This is because if you look at the image, there is big gap between the 14 column groups, so we split them up into two groups. Same for the rows, there seems to be some sort of divider, so we can’t hardcode the rows.

Rompar IR screen

The first GUI screen we are presented with is this targeted infrared screen that will highlight the dark and light spots of what could be a doped transistor vs a missing one. We can adjust the threshold by going to CV Options -> Pixel Threshold. Adjust it until we can see something like this:

Close up of bits in IR mode

We need the program to recognize that the first row is going to return 0000001 and the second row returns 01110101. Keep in mind, by convention, the brighter areas are shown as 1 and darker areas as 0. This is inverted as needed during post processing if it’s reversed into an actual binary. Now, let’s move over to Display -> Base Image -> Original. Next, we want to grid the columns and rows, so we ctrl+click column 1, then go to column 16, and do the same. We are going to repeat this until we cover every column. Eventually, we should end up with a grid that looks like this:

Blue grid for ROM columns

The blue lines are a bit faint but once you zoom in you will see them a lot more clearly. Now, let us highlight the individual bits. Hit cmd+click on each row, and it will look like:

Circled binary bits

As you can see there are a few mistakes, we can fix individual bits by going to Edit -> Mode -> Data Edit Mode. Now, ctrl+click each individual bit to turn them from blue to green or vice versa. The program recognizes green markers as a ‘1’ and blue markers as a ‘0’. Unfortunately, with this ROM image, I had to do a lot of manual editing but once you feel it is at a good place, we can export this to a binary bit matrix by going to Data -> Export Data as Text. You’ll end up with a file that is holding all your binary data as you can see in my Github.

Decoding the Bits

Now that we have our bit matrix file, it’s time to convert that into a readable and disassembled firmware file. We have one of two ways to go about doing this, zorrom or bitviewer. Generally, if we already know the architecture, use zorrom, a utilities tool to convert the data between physical and logical representations of a chip’s memory layout. Zorrom’s README states, “For example, a photograph of a boot ROM that has been converted into a 2D array of bits (.txt) can be converted into a machine readable binary (.bin). This .bin can then be emulated, disassembled, etc. as you’d do with any normal firmware file”. It has a great API to write and customize how the ROM should be read, i.e., the layout, endianness, whether or not it requires bit flipping, and output bit orders.

The reason why we can’t start off using zorrom is because we do not know the processor type. After days and weeks of research on the parent corporation, WCH, I couldn’t get anything that closely resembled a 14-bit architecture. Considering what we are dealing with, we might need a bit of visual brute forcing with a tool like bitviewer. With this tool, the only adjustment we would have to make is to pad the bit matrix file to a 16-bit architecture. This software doesn’t seem to do well with 14 column groups, that’s okay though because when we extract the bin file, those padding bytes will not impact the actual firmware bytes.

Generally, when we are dealing with situations where we don’t know which byte represents what op-code, we can’t just disassemble test bin files. Instead, we look for hardcoded strings in the file or some sort of constants that would represent internal configurations. Often, you really have to study the circuitry to understand what is going on. I’m still fairly new to this process but I always start with the connections coming out of the ROM itself.

Metal layer (left), Substrate layer (Right)

Here is what we can gather from this picture. The ROM has 64 bits vertically and 16 x 14 horizontally; as I said before, almost a 2k ROM. I figured it needs 10 address bits in total. The first column of transistors on the vertical address bits alternates between address bit 0 and not address bit 0. The next column switches every 2 bits, and so on. Each address will activate one row. I believe that it is showing 4 bits horizontally and 6 bits vertically. Each line must have a complement to possibly make decoding easier by the 16:1 multiplexers. If you zoom real close, you will see the multiplexer saves room to add 14 inverters rather than run an extra horizontal signal line.

Now we can open up bitviewer with our newly padded bit matrix file. Below you’ll see the first mapping of what bitviewer thinks the bits should be laid out by bits per row, and columns per output.

Bitviewer opening screen

The bits per row seem to be correct but I know it’s not columns of 32 bits, I have found that each column group contains 16 bits per column through the silicon images. All we have to do is adjust that number, and the view will automatically change.

Bitview 16-bit columns

The next item on the list now would be to look at the hex dump and see if we see any sort of clue that we have the right order set by clicking on the Byte view (hex) button. While we scroll the bits, you’ll notice nothing looks recognizable because 1) we do not know what the opcodes should look like because we do not know the architecture, and 2) we don’t see any hardcoded strings yet. Therefore, we are banking on the fact that we will see some strings in here to tell us once we decode it correctly.

Let’s make some adjustments by hitting the Export Options button. You’ll notice here we can adjust the ROM layout, endianness, output bit orders, and some additional changes like reversing the bit order. We are going to keep most of the options the same, including the bit order, which should look like this: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15. Sometimes you have to try a few things in order to get the output right, but after some experimentation I found checking these boxes helped: the Reverse output bit order, and the Address run right-to-left. Now we can scroll through the hex view again.

05C0: FE 73 FF DB EF ...  .s...t...t.b.|.j
05D0: FE 50 C6 5F D6 ... .P._._.Q...P....
05E0: DD 74 DF F8 ED ... .t...&.m...S.p..
05F0: FF 6D ED 00 FF ... .m...y...|.....>
0600: FF 7A FF 6A ED ... .z.j.<.g.Z.X.s..
0610: D9 74 CE 65 ED ... .t.e...W.p...[..
0620: E6 F0 F5 5B F0 ... ...[.W.W.W.W....

Still nothing throughout. There is another option we have not explored… our bits might need to be inverted/flipped. Meaning our ‘1’s have to be ‘0’s, and vice versa. Luckily, bitviewer allows us to do this; we click on the Select all button which will highlight all the bits in each row and column. Once highlighted, press Invert Sel.

Inverted bits from Bitview

Everything was successfully inverted and now we can open up the byte viewer tab to check our results.

0770: 10 03 10 09 ...  .............U..
0780: 10 53 10 00 ... .S...B...2......
0790: 10 30 10 00 ... .0...-..3...3...
07A0: 33 F3 10 00 ... 3...3...3...3...
07B0: 2F A4 10 00 ... /.....(.....+...
07C0: 10 23 29 08 ... .#)...../.. .'/.
07D0: 10 02 10 03 ... ..../.....+..P.S
07E0: 2F A4 10 72 ... /..r.e/..i.r/..n
07F0: 10 6D 2F A4 ... .i/..t.a+.. .l..

This was not noticeable to me at first but we got a valid firmware file to explore! Looking at offsets 0x0770 and 0x0780, we can make out the string USB 2.0. What else? The next one is not so easy to see at first because there are two strings playing leap frog. The strings Print & Serial are at offsets 0x07D0–0x07F0. Other clues we would look for would be at the top of the firmware file such as some sort of JMP table and/or initialization vector table. This would tell us there are repeated instructions, or in our case, bytes.

JMP table

Although we still do not know what architecture this is or what the assembly would even look like, we can see some sort of pattern at the beginning which is what we would want to see at the beginning of any valid firmware file. Let’s dump this into a firmware bin file by clicking on Save bin. From here, we will have to do a good amount of guessing and circuit reverse engineering to really understand how we can turn these bytes into readable assembly code.

Side Note: Back to using zorrom, now that we know what options we needed to make this a proper firmware file, we can automate this by writing the correct algorithm. Using zorrom’s API, all a user would have to do is feed it a binary text file, and it will spit out a firmware file. You can see the code I wrote for the CH340 here. You can run the tool like this:

➜ python3 txt2bin.py --arch ch340t ch340_binary.txt ch340_fw.bin

Also note that with zorrom, we do not need to pad our binary text file to have 16 column groups, it will do fine with our original 14. Keep in mind, sometimes you will have the wrong orientation when extracting bits from images due to mirroring mistakes or how the chip should be correctly rotated. If we dump a binary text file like the one linked above, we can use a tool like rotate to basically flip the text in the file upside down.

Disassembling the Unknown

The biggest question thus far is how do we disassemble something that we do not know the architecture for? If it weren’t for the hard-coded strings we found, how would we have known we got the bytes in the right order? Well, the answer is not so simple or straightforward. We are going to use IDA Pro as our main disassembler tool but we will have to write our own custom plugin.

When dealing with unknown architecture, you have to study the images a bit to get an idea of how the ROM is being used. A good comparison would be, if a doctor were to get a sick patient and did a CT scan of the whole body to find what was wrong with the person, and was looking at every single organ to find clues.

So in our case we look at the whole chip, and not just the ROM:

Delayered CH340 with Annotations

Annotation is a big part of ICRE design, and in our case we had a pretty good amount to go over. Reading the datasheet, we know this chip supports USB capabilities, as it is a USB to USART bridge. We also know that this chip needs an external oscillator source for clocking. Some assumptions we can make on the chip is that there is going to be: a block for SRAM for volatile data storage, an area for registers that would be used to accept, store, and transfer data, and instructions that are being used immediately by the ROM to Processor Core. As there is no EEPROM on this chip according to the datasheet, we marked the large area to the left for how the data gets received and transmitted by the USB port.

As mentioned before, IDA does not have a processor plugin for this (because it is an unknown architecture), my colleague Chris and I started to write our own plugin which is still in progress. A couple of starter assumptions have to be made when put in a situation like this. For example, the first few bytes of the firmware should be a repeating pattern, so it must be some sort of small jump to the addresses table.

Partially disassembled view of the ch340 code

You can see from the python code, we were also able to nail down how the hardcoded strings were being transferred. Similar to JMP opcodes, CALLs will be fairly similar when viewing a hexdump of unknown firmware type; you’ll see an instructional byte, then another two bytes dedicated to the address.

There is still a lot more work to be done. Even though we are under the assumption that the relationship from instruction to register is in big endian format, we had to switch it to little endian because for some reason IDA is not converting or disassembling the two byte WORD chunks properly when in big endian format. This will most likely end up being a two part series where I can finally explain how everything works on this chip. I’m going to conclude this paper with how we clean up the mess we made in the lab with all the inorganic acids we have been using.

Cleanup: Acid-Base Neutralization

Sulfuric acid (H₂SO₄) can be neutralized by using an extremely strong base called sodium hydroxide (NaOH). This acid reacts with NaOH to produce sodium sulfate (Na₂SO₄) and water. This is an acid-base neutralization reaction which after balancing the equation, we can see it will need a proper 2:1 ratio of basic solution to acid.

2NaOH (aq) + HSO₄ (aq) → 2HO (l) + NaSO (aq)

In our case, we will be using a much higher ratio because if you remember we only used 20mL of H₂SO₄ to decapsulate our chip. NaOH comes in a salt-crystal form, and only 15 grams of NaOH per 150mL of water (10% solution) will suffice when mixing the solution together. NaOH + H₂O will produce a solution of cation Na+ and anion OH-, additionally some heat is released. We can work out the equation as such:

NaOH + 2H₂O → Na+ + OH- + H₂O (delta H < 0)

Pour the acid slowly into the newly made base solution. You might notice the temperature rising a bit while doing this, this happens because the process is exothermic, meaning that a superfluity of heat is produced by the acid-base reaction. This reserved heat is the net result of the processes bond-breaking and bond-making. To put it simply, the final products formed through the reaction, are in a lower energy state than the reactants, and the contrast in energy between the two states is equal to that given out as heat. We can dilute the rest by adding another 150mL of water on top of our acid-base mixture. Stir slowly for about 2 minutes, then we can use litmus paper to check neutrality.

testing pH levels of neutralized sulfuric acid

Side note: In order to check neutralization, we can use household items like sodium bicarbonate (NaHCO₃) to drop into our beaker, and check if the liquid will fizzle or not. If the diluted acid-water starts to fizzle, that means it is still a bit acidic due to the trapped carbon dioxide (CO₂) attempting to escape the carbonic acid solution. After everything is all settled, put all the chemicals used in a waste container, and contact your local chemical waste disposal company to dispose of everything for you.

Thank you for following along! I hope you enjoyed it as much as I did. If you have any questions on this article, please DM me at my Instagram: @hackersclub or Twitter: @ringoware

Happy Hunting :)

References & Thank You’s



Ryan Cornateanu

Security Researcher | Reverse Engineer | Embedded Systems