-2

If I have a function like this

void check(char *buffer)
{
    //some stuff that only needs to be run once
}

Is there any way I could go about 'deleting' this function from memory after I call it once?

I could easily go about doing this in pure ASM. But I was wondering if there is a more standard way of doing it.

thehaxdev
  • 71
  • 8

2 Answers2

8

The C++ standard (or even the C one) consider the totality of the code of your program as immutable. So it makes no sense to "delete" a function.

But let's be pragmatic. Most of the time your C++ program is compiled into some executable running in some operating system (I'll suppose that OS is Linux, but you could adapt my answer to other OSes). For a general overview on OSes, read Operating Systems: Three Easy Pieces (freely downloadable)

Then, when your program is starting (probably by your shell doing a fork(2) then an execve(2)) in some process, a new virtual address space is created (and managed by the kernel which configures the MMU and handle page faults by paging appropriate data e.g. from disk).

In general, the ELF executable of your program contains a code segment which is memory-mapped in some read only and executable segment of your virtual address space. Since that part is read only you cannot "delete" or "write" it. With shared libraries the virtual address space is more complex and contains several code segments (perhaps one or more per library).

I could easily go about doing this in pure ASM.

Wrong for a program running on some (modern) OS. Because the code segment is read-only, so you need to alter your virtual address space (the only way to do that is thru appropriate system calls like mmap, munmap, mprotect; you can invoke them in C++, in C, or even in ASM). The fact that some function has been coded in assembler don't change that. And even an assembler code cannot overwrite the code segment (you'll still get some segmentation fault in that case) without altering your virtual address space before.

You could play low-level tricks, for example use the munmap(2) system call -from some C++ code or C code or assembler code -, to alter your virtual address space and remove some pages from it. I strongly recommend not doing that. But if you go that route, you need to understand all the details of your ELF executable and shared objects and of your virtual address space. Be aware of ASLR, elf(5), ld-linux(8), vdso(7). Use objdump(1) & readelf(1) & see ld(1) to understand the details of your ELF executable.

You might put your code (of your check and several other functions) into some plugin, but in your case you probably should not. In that case, you could dynamically load that plugin and later unload it. You'll use dlopen(3) to load the plugin, and dlclose to unload it. The dlopen-ing of a plugin does several mmap(2) (so grows the virtual address space) and process relocations and its dlclose does several munmap(2) (so shrinks the virtual address space).

Play with /proc/ on Linux (see proc(5)). Try cat /proc/self/maps and cat /proc/$$/maps and cat /proc/$$/status in a terminal. And read /proc/self/maps from your C++ program (for example, see this).

For example, fish is a shell coded in C++. It runs in the process of pid 4735 right now on my desktop. Its virtual address space has 69 segments, as counted with wc /proc/4735/maps. Its code segment is mapped (read only and executable):

556dad571000-556dad697000 r-xp 00000000 08:01 11272485  /usr/bin/fish

the code segment of my C standard library is probably at

7fa96bcb3000-7fa96be4c000 r-xp 00000000 08:01 1704209  /lib/x86_64-linux-gnu/libc-2.25.so

I don't understand your security concerns (mentioned only in a comment, which should go into the question), since your code segment is read-only. Indeed, a malicious function could change the protection of some code segment (using mprotect(2)) and then alter it. But you need to explain how the code injection (of that malicious function) happens in the first place (you can code a program relying on process isolation and proven with formal methods to avoid that happenning, assuming that your Linux kernel is trustable).

Maybe you have privacy concerns (but that makes a different question). Obviously you don't want to keep passwords as clear text in your code.


Maybe (but quite unlikely) you are coding a standalone program (without any OS) e.g. for an embedded system and its microcontroller (e.g. Arduino like). In that case, your code is in flash memory. And how to alter or delete it becomes hardware specific. BTW, overwriting flash memory several times is harming the hardware.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • You can also do `mprotect` to unprotect the page and overwrite the function body with a `nop` sled to a final `ret`. This avoids the need to unmap a whole page. – Matteo Italia Nov 22 '17 at 06:21
  • Yes, but why would you do so? By default code segments are read-only! – Basile Starynkevitch Nov 22 '17 at 06:22
  • 1
    Will you also address OP's concern for security? As in, should you worry or not. – Passer By Nov 22 '17 at 06:27
  • @BasileStarynkevitch Why would you say I'm wrong about being able to do this easily in ASM? Its very easy, I replace all instructions within the sub with a nop. Which works fine for what I'm doing. I was asking this question to see if there is a way in C++ to do this. Which I've learned you can't. Either way thanks for the long response with useful info. – thehaxdev Nov 22 '17 at 07:44
  • Because coding in ASM don't change anything about your virtual address space and the read-only property of code segments. The only way to change your virtual address space is to use relevant system calls (`mmap`, `mprotect`, `munmap`). ASM has nothing to do with it (even if you obviously could do system calls in ASM) – Basile Starynkevitch Nov 22 '17 at 08:18
  • My point is that you should understand that it is *not* a matter of coding in ASM vs C++. It is a matter of understanding your virtual address space and using cleverly system calls to alter it. It is irrelevant if you call these system calls from ASM or from C++ (or Rust, or C, or D, or Go, etc...). Probably for you, using C++ is simpler. I see no reason to use ASM. Just replacing some machine code with `nop` won't work (because your code segment is read-only, so you'll fail doing that, even in ASM) – Basile Starynkevitch Nov 22 '17 at 08:29
  • You probably need to read an entire book about OSes (or at least several chapters related to processes and virtual memory). I cannot explain all that in an answer on SO – Basile Starynkevitch Nov 22 '17 at 08:38
  • @BasileStarynkevitch nope Ive tested and it works fine. The operating system I am working on easily allows you to directly modify the memory of an executable once it is already running. If you try to call the function after it is NOPd out your system will crash, but thats the whole point. – thehaxdev Nov 23 '17 at 13:42
1

You can do it like this, with a slight performance hit (irrelevant if you want to call the function just once):

In .c file:

static void check_private(char *buffer)
{
    //some stuff that only needs to be run once
    check = NULL; // disable calling this ever again
}

// declare check as pointer to function (pointer to char) returning void
// and intialize to check_private
void (*check)(char*) = check_private;

In corresponding .h file:

extern void (*check)(char*);

As long as you strip debug symbols, and don't store it elsewhere in the program, check pointer will be the only place where address of check_private is stored, so once you overwrite it, it is gone.


NULL is a decent disable value if you know you are running on a normal OS with memory protection, as the UB it will produce will be segfault then, and it is easy to check.

If you don't want segfault, you could set it to point to another function, for example:

static void check_disabled(char *buffer)
{
    (void)buffer; // disable compilation warning about unused parameter
    abort(); // maybe this
    exit(EXIT_FAILURE); // or this
    return; // or this for no-op
    for(;;); // or even this on some tiny embedded system
}

If this is a library and you want some kind of rudimentary hard protection against invalid use, then instead of a pointer, you could have this exported function:

void check(char *buffer)
{
    static int called = 0;
    if (!called) {
        called = 1;
        check_private(buffer);
    } else {
        // called 2nd time, do something, see above for suggestions
    }
}

Again, as long as debug symbols are stripped, only place where address of called variable is stored is inside this function's code, so there is no way to gain access to it. Well, except by invoking UB of somehow guessing its address and using a pointer to modify it, but especially with modern optimizing compilers, this really is UB and not just hack, as there's no guarantee what assembly and data compiler actually produces for this C code.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • But the initial code of `check` and/or `check_private` is (usually) still in the code segment. So I don't feel this answer addresses the (confusing) concern of OP – Basile Starynkevitch Nov 22 '17 at 09:12