Why don't computers enforce immutable address spaces for execution?



  • A piece of malware detects signatures of the sandbox an AV solution tries to use to fingerprint malicious behavior and pretends to be innocent. Once in the real OS environment, it then downloads executable data encoded as image data and decodes the executable bytes from the image, storing them in memory out of order, preventing any attempt at scanning the memory range for malicious code. The program then executes those instructions in the correct order, without the AV ever being able to scan them (even statically).

    This scenario seems to me, with my limited experience in the field, to pose a problem for malware defense solutions.

    I'd like to ask what methods are used to defeat this kind of attack, and also pose my own idea:

    The kernel / os / processor (not sure which would be capable) imposes a restriction: A program must present its executable data which will become an immutable memory range that the instruction pointer cannot leave. Why is this not a good solution?



  • If you're proposing that the process can only present this executable data once, then it breaks an enormous range of things:

    • All software that dynamically loads libraries, be they system libraries that are different by platform version, plug-ins / mods, or simply code that keeps its memory footprint small until it knows what libraries it'll need.
    • All software that performs any type of just-in-time compilation (this rules out all modern JS engines, most Java and .NET runtimes, probably many database engines, and a bunch of other stuff that optimizes executable instructions of any sort).

    Those are really important categories of software. You could accept the loss of JITs if you don't mind horrible performance costs, but think about how much of the web runs on Node now and how bad it would be if suddenly all that code was a tenth as fast?

    Also, that wouldn't stop the malware anyhow, it'd just arrange the payload code and then create a new process whose entry point is that code. You could maybe stop that too, but it's tricky, then you're running into lots of other things like normal compilers.

    Even just a rule that, once code is ever marked executable, it's forever non-writable, that's still going to cause problems for the entire category of debuggers (setting a breakpoint typically requires modifying the executable code, hardware breakpoints exist but they're very limited, and some other stuff debuggers do is even less practical). Also that clearly isn't enough to stop malware, because it'll just create new executable ranges at need.


    Conceptually, the stuff you describe is possible today (all modern CPUs can enforce "instruction pointer can't leave [range]", that's what NX/DEP is, so it's just a matter of the OS not allowing programs to remap memory to executable or executable to writable). However, that really doesn't stop things. For example, one of the best techniques for bypassing DEP - Return-Oriented Programming (ROP) - already operates on the fact that any non-trivial program has enough "gadgets" (instructions or instruction fragments that, if you put the instruction pointer at the right place, do something useful and then jump or return to an address determined by writable stack or heap memory, said address containing the next gadget) to build a Turing machine. It might be slow and inefficient, but you can re-purpose the x86[_64] binary executable code in any non-trivial program to run literally any software, restricted only by the limitations of RAM (including stack space), CPU time, and process privileges.

    There's also a thing where you seem to be focusing on trying to stop malware by writing a better antivirus and blocking ways around it. The problem is, AV is fundamentally a reactive defense - it can't do anything against novel attacks, and anything with too sensitive of heuristics will throw an unacceptable level of false positives so mostly AV just looks for known malware - and getting around AV is easy. Most malware isn't written with enough mutational capacity to avoid AV once the malware is recognized, but that's almost certainly a problem with a generalizable solution (if nothing else, malware could be written as a return-oriented program, which does nothing suspicious until it "accidentally" slips up a jump and lands in the middle of an opcode and starts doing something totally different) and so, at best, you're massively disrupting the entire software industry to just try to raise the bar for malware a little.



Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2