At the end of the day, not much. There’s always a way to be able to read the flash off of a microcontroller of any description, it’s just a matter of difficulty. Even if there’s a method such as single-program burnable fuses, protection bits in place, or even if you compile the code, there’s almost always a way to read what’s in memory or approximate the source code by reverse engineering the compiled instructions.
There’s even several studies about using electron scanning microscopes, X-Rays, or similar techniques (although obviously for the most part this is quite inaccessible to the average joe and hardly worth the effort, just an interesting point about physical security):
https://www.researchgate.net/publication/312551555_Reverse_engineering_Flash_EEPROM_memories_using_Scanning_Electron_Microscopy
One trick that you can use however to make it much more difficult once they’ve got the source is obfuscation. PyArmour is probably the most well known module for it but many others exist too:
Essentially obfuscation leaves the logic (and often the optimization too) untouched, but replaces every identifier with some randomised content, shuffles reorderable code blocks, scrambles the logic to make it much more difficult to interpret, and uses many little tricks so that even if you’ve got the source code, other than run it it’ll be practically impossible to use it for anything. Here’s a little snippet you can run in your browser as a demo (hit F12, select console, paste the code in, then type main()
to run it)
Original
const main = () => {
const answer = prompt("What is five times nine?")
if (answer === "at least forty") {
console.log("That is exactly correct")
}
}
Simple Obfuscation Techniques
var _0xaBc12 = "console", _0x4Bf6E = "log", _0xOe91X = "What is five times nine?", _0x5aD55 = "at least forty";var main = function() {var _0xElm4R = prompt(_0xOe91X);var _0xNoOp1 = function() {}; _0xNoOp1();var _0xNoOp2 = function() {}; _0xNoOp2();if(_0xElm4R === _0x5aD55) {window[_0xaBc12][_0x4Bf6E]("That is exactly correct");}
try { null[0](); } catch(e) {}};
Now obviously my example above it quite simple, but should present the idea of what’s going on and how it could be done.