Self-Modifying Binary)

Is it possible to create a self-modifying binary on Go? For example, the program should display how many times it was run and the time of the last run.

Admit:

./goprogram
1 29.06.2019 14:10
./goprogram
2 29.06.2019 14:12
./goprogram
3 29.06.2019 15:10
./goprogram
4 30.06.2019 14:10

At the same time, no additional files or records should be created.

On Purebasic, it is quite easy to implement this:

  • The compiled binary file creates its own copy.
  • Writes a number to the end of the file.
  • Replaces the original a modified copy.
  • When you run it again, it outputs the recorded number.

Can I have an example of such an implementation on Go?

Author: Ainar-G, 2019-06-29

2 answers

What you want is not safe, and will probably be banned by antivirus programs and other security systems. Not to mention the difference in different operating systems. However, it is possible to do something like this (error handling is omitted for brevity):

type persistent struct {
    Magic   [8]byte
    Content int64
}

var p = persistent{
    Magic:   [8]byte{0xBA, 0xDD, 0xFA, 0xCE, 0xBE, 0xEF, 0xCA, 0xCE},
    Content: 0,
}

func main() {
    fmt.Println(p.Content)

    const size = int(unsafe.Sizeof(p))
    currentBuf := bytes.NewBuffer(make([]byte, 0, size))
    err := binary.Write(currentBuf, binary.LittleEndian, p)

    newP := p
    newP.Content++

    newBuf := bytes.NewBuffer(make([]byte, 0, size))
    err = binary.Write(newBuf, binary.LittleEndian, newP)

    currentBytes, newBytes := currentBuf.Bytes(), newBuf.Bytes()
    self, err := os.OpenFile(os.Args[0], os.O_RDONLY, 0755)
    selfBytes, err := ioutil.ReadAll(self)

    i := bytes.Index(selfBytes, currentBytes)
    copy(selfBytes[i:i+size], newBytes)
    newSelf, err := ioutil.TempFile("", "selfmodifying")
    _, err = newSelf.Write(selfBytes)
    err = os.Rename(newSelf.Name(), self.Name())
    err = os.Chmod(self.Name(), 0755)
}

Full example on Playground: https://play.golang.org/p/WQjUQb6X7es.

 2
Author: Ainar-G, 2019-06-29 09:20:35

It seems to me that the possibility of implementation depends heavily on
1) specific OS and FS
2) definitions of the concept of" no additional files "(by the way, "no", if according to the usual rules of the language, it is necessary to write together; I mention this because it is generally a problematic interpretation, which means that it is necessary to clarify this).

For example, if it is acceptable that a temporary copy is created when the counter is updated (and there are rights to create it in this directory), then:

  1. You need to enter some information add a unique signature to the file. This is any constant, string or binary-to your taste, as long as a set of random bytes, to which the actual counter will be assigned at a known offset. At the program level, this signature is a global variable.
  2. On the update, the program creates a copy of its prog in the form of prog.new, searches for this constant, and updates the counter value based on the offset calculated in this way.
  3. Read on execution - accesses the variable in which this constant is stored, and retrieves the counter from it.

In general, it is more reliable with strings - because binary structures can have alignment, but you can search in them with slices of a binary sequence.

If there is no such thing, then it is usually impossible to write to the file of the program currently running - Unix does not allow this (all flavors), Windows seems to do the same. But you can make an entry in the additional attributes of the file (check whether it will work in a particular case). Linux has the xattr subsystem and changing these attributes is not blocked by the fact that the program is running.

In any case, note that this can be blocked by a variety of methods and conditions - read-only FS, read-only directory, FS state tracking daemon, etc.

 1
Author: Netch, 2019-07-03 05:05:51