{"id":18793387,"url":"https://github.com/89luca89/pakkero","last_synced_at":"2025-04-09T12:05:08.680Z","repository":{"id":45785427,"uuid":"265665497","full_name":"89luca89/pakkero","owner":"89luca89","description":"Pakkero is a binary packer written in Go made for fun and educational purpose. Its main goal is to take in input a program file (elf binary, script, even appimage) and compress it, protect it from tampering and intrusion.","archived":false,"fork":false,"pushed_at":"2023-01-16T21:07:13.000Z","size":1701,"stargazers_count":258,"open_issues_count":2,"forks_count":43,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-02T09:08:57.853Z","etag":null,"topics":["compression","elf","elf-binaries","encryption","golang","launcher","packer","payload","protector","upx"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/89luca89.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-20T19:19:26.000Z","updated_at":"2025-03-18T11:32:14.000Z","dependencies_parsed_at":"2023-02-10T06:15:39.571Z","dependency_job_id":null,"html_url":"https://github.com/89luca89/pakkero","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/89luca89%2Fpakkero","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/89luca89%2Fpakkero/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/89luca89%2Fpakkero/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/89luca89%2Fpakkero/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/89luca89","download_url":"https://codeload.github.com/89luca89/pakkero/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248036063,"owners_count":21037092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compression","elf","elf-binaries","encryption","golang","launcher","packer","payload","protector","upx"],"created_at":"2024-11-07T21:24:44.550Z","updated_at":"2025-04-09T12:05:08.660Z","avatar_url":"https://github.com/89luca89.png","language":"Go","funding_links":[],"categories":[":package: Packers"],"sub_categories":["After 2010"],"readme":"# Pakkero\n\n\u003cimg src=\"pics/logo.jpg\" data-canonical-src=\"pics/logo.jpg\" width=\"250\" height=\"250\" /\u003e\n\nCredit: [alegrey91](https://github.com/alegrey91) for the logo! Thanks!\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/89luca89/pakkero)](https://goreportcard.com/report/github.com/89luca89/pakkero)\n[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html)\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2F89luca89%2Fpakkero.svg?type=small)](https://app.fossa.com/projects/git%2Bgithub.com%2F89luca89%2Fpakkero?ref=badge_small)\n\n## Introduction\n\n**Pakkero** is a binary packer written in Go made for fun and educational purpose.\n\nIts main goal is to take in input a program file (elf binary, script, even appimage) and compress it, protect it from tampering and intrusion.\n\nIt is not recommended for very small files as the launcher itself can vary from ~700kb to ~1.7mb depending on compression. On files above 2.6mb there is gain, else the resulting binary is larger than the original:\n\n```\nbase-bin    1.2M    -\u003e  1.6M\nsmaller-bin 2.4M    -\u003e  2.3M\nsmall-bin   3.7M    -\u003e  3.0M\nmedium-bin  25M     -\u003e  16M\nbig-bin     148M    -\u003e  88M\n```\n\nWith compression disabled, all resulting file size are ~1mb higher, making it suitable for 5+mb files.  \n\n#### How compares to UPX?\n\nTested with a 24mb binary file (I didn't have a big project handy so I just concatenated a bunch of programs from `/usr/bin/` to make one big elf) became:\n\n- 12mb using `upx -9`\n- 13mb using `pakkero -c`\n- 14mb using `pakkero` without compression\n\n## Install\n\nIf you have a [Go](https://golang.org/) environment ready to go, it's as easy as:\n\n```bash\ngo get github.com/89luca89/pakkero\n```\n\nOnce you retrieved you are ready to build:\n\n```bash\ncd $GOPATH/src/github.com/89luca89/pakkero; make\n```\n\nor to test\n\n```bash\ncd $GOPATH/src/github.com/89luca89/pakkero; make test\n```\n\nThe binary file will be in `$GOPATH/src/github.com/89luca89/pakkero/dist`\n\nThe following are hard dependencies:\n\n```\n - go -\u003e to build the launcher\n - ls\n - sed\n - strip -\u003e to strip the launcher\n```\n\nThe following are weak dependencies\n\n```\n - upx -\u003e needed for launcher compression (optional)\n```\n\n**GO 1.13+ needed**\n\n**Dependencies are checked at runtime and an error message will specify what is missing**\n\n# Disclaimer\n\n**This is a for-fun and educational project**, complete protection for a binary is **impossible**, in a way or another there is always someone that will reverse it, even if only based on 0 an 1, so this is more about exploring some arguments that to create an anti-reverse launcher.\n\n---\n\nPakkero is divided in two main pieces, the packer part (Pakkero itself) and the\nlauncher part.\n\n## Part 1: the packer\n\nPakkero can be launched like:\n\n```bash\npakkero --file ./target-file -o ./output-file -register-dep dependency-file -c\n```\n\n![demo](pics/demo.png)\n\n### Usage\n\nTyping `pakker -h` the following output will be shown:\n\n```bash\nUsage: pakkero -file /path/to/file -offset OFFSET (-o /path/to/output) (-c) (-register-dep /path/to/file)\n  -file \u003cfile\u003e          Target file to Pack\n  -o   \u003cfile\u003e           place the output into \u003cfile\u003e (default is \u003cinputfile\u003e.enc), optional\n  -c                    compress the output to occupy less space (uses UPX), optional\n  -offset               Offset where to start the payload (Number of Bytes)\n  -enable-stdout        Whether to wait and handle the process stdout/sterr or not (false by default, optional)\n  -register-dep         /path/to/dependency to analyze and use as fingerprint (absolutea, optional)\n  -v                    Check pakkero version\n```\n\nBelow there is a full explanation of provided arguments:\n\n* **file**: The file we want to pack\n* **o**: (optional) The file output that we will create\n* **c**: (optional) If specified, UPX will be used to further compress the Launcher\n* **offset**: (optional) The number of bytes from where to start the payload (increases if not using compression)\n* **enable-stdout** (optional) whether to enable or not the handling of the stdout/err of the payload \u003cu\u003e**disabled by default, less secure**\u003c/u\u003e\n* **regiser-dep** (optional) Path to a file that can be used to register the fingerprint of a dependency to ensure that the Launcher runs only if a file with similar fingerprint is present\n* **v**: Print version\n\n### Packaging\n\n**The main intent is to not alter the payload in any way, this can be very important\nfor types of binary that rely on specific order of instructions or relatively fragile timings.**\n\nThe target of pakkero is not to touch the \"payload\".\nOther packers like UPX works by compressing the sections stored within the Section Table of the executable file, relocating the sections and renaming them. It then alters \nthe entry point where the binary will run. While this is really what defines storically a packer, this in some way or another \"touches\" the payload so can make it unusable (when it works on strict timing, precise elf sections tricks and so on)\n\n#### Building\n\nTo build the project, you can simply use the `Makefile`;\n\n- `make` will compile\n\n- `make test` will compile and run a run with a simple binary (echo)\n\n**Why not using simply go build?**\n\nGo build works fine, but will skip a fundamental step in the building process, **the injection of the launcher stub inside Pakkero source**\n\nThis way the Pakkero binary has inside the source of the Launcher to be used for each packaging.\n\n#### Building using Docker\n\nBuild Pakkero image:\n\n```sh\nsudo docker build . -t pakkero\n```\n\nRun containerized Pakkero:\n\n```sh\nsudo docker run -it -v \u003ctarget_dir\u003e:/ext pakkero --file /ext/\u003ctarget_elf\u003e -o /ext/\u003ctarget_elf\u003e.packed\n```\n\n#### Building using Podman\n\nBuild Pakkero image:\n\n```sh\nsudo podman build . -t pakkero\n```\n\nRun containerized Pakkero:\n\n```sh\nsudo podman run -it -v \u003ctarget_dir\u003e:/ext pakkero --file /ext/\u003ctarget_elf\u003e -o /ext/\u003ctarget_elf\u003e.packed\n```\n\n#### Payload\n\nFor this purpose the payload is simply compressed using zlib then encrypted using AES256-GCM\n\nDuring encryption, some basic operations are also performed on the payload:\n\n- putting garbage random values before and after the payload to mask it\n- reverse it and change each byte endianess\n\nEncryption password is the hash SHA512 of the compiled launcher itself together with the garbage values added to fill the file till the offset, thus providing\nsome integrity protection and anti-tampering.\n\n#### Offset\n\nThe offset will decide **where in the output file the payload starts**.\n\nPut simply, after the launcher is compiled (more on the launcher later), the payload is\nattached to it. The offset ensures that the payload can be put anywhere after it.\nAll the space after the launcher until the payload is filled with random garbage.\n\n![payload](./pics/decryption.png)\n\nBeing part of the password itself, greater offset will make stronger the encryption, but\nenlarge the final output file.\n\nOptimal value are **at least** 800000 when compression is enabled and **1900000** when disabled. *If not specified a random one will be chosen upon creation.\n\n### Obfuscation\n\nThe final thing the packer does is compiling the launcher. To protect some of the fundamental part of it (namely where the offset starts) the launcher is *obfuscated* and heavily stripped down.\n\nThe technique utilized for obfuscating the function and variables name is based on typo-squatting:\n\n![obfuscation](./pics/obfuscation.png)\n\nThis is done in a pretty naive way, simply put, in the launcher each function/variable which name has to be obfuscated, needs to start with the suffix **ob**, it will be then put into a secret map, and each occurrence will be replaced in the file with a random string of length 128, composed only of runes that have similar shape, namely:\n\n```go\n    mixedRunes := []rune(\"0OÓÕÔÒÖŌŎŐƠΘΟ\")\n```\n\nFor pure strings in the launcher, they are detected using regular expressions, finding\nall the words that are comprised between the three type of ticks supported in go\n\n```\n`\n'\n\"\n```\n\nAll of the strings found this way, are then replaced with a function that performs a simple operation of reconstruction of the original string:\n\n```go\nfunc ÓΘŌOÒŐÒŌÓÒOŎΘOΟ0ŐÒÖŎÕΟΘÕÓÕÓŎÓŌÕ0ŎŌΘÕŎÕ() string {\n    ŌÒ0ŎŎŐÓÖÖΘO0ŌŌŌÒŌŌÒƠÔÖΘŐÖΟŎƠƠ00Õ0ÖÕ0ÖŐŐÓΟŌΟ := []string{\"Ò0ƠŐÖŐŎΘÖƠÔÖÕÓΘÕÕŌŎŐƠÔΘΘƠ\", \"ÔÒÕΟƠŐÒŌOƠÖ\", \"ƠΘŐÒƠΘŌ00ΘΘΟÔŎŎΘŐƠŐΘŎΟÕÖÕÖΟÖΘÒÖ\"}\n    var ΟŐÔÒÖÔΘÕÔŎÒÓÖÖÒΘ0ÖÔΟÖŎ0ÔOÓÖƠŌÔÓŌŌ []byte\n    for _, ŌΘƠÔÕÔΘÔOÔOŐΟΘŌΘƠÔÕÔΘÔOÔOŐΟΘ := range ŌÒ0ŎŎŐÓÖÖΘO0ŌŌŌÒŌÕÒÔÕŌŎ00ÔÔÒOƠÓÕÔÒ0ΘƠΘŐ0OOŎÓÒŐ0Õ0ÓOÕÓŐƠŌŎÕÖ0ÕÖÔŌΟΟŌÒÖÒÖOΟOÒ0ÖŐŐÓΟŌΟ {\n        ΟŐÔÒÖÔΘÕÔŎÒÓÖÖÒΘ0ÖÔΟÖŎ0ÔOÓÖƠŌÔÓŌŌ = append(ΟŐÔÒÖÔΘÕÔŎÒÓÖÖÒΘ0ÖÔΟÖŎ0ÔOÓÖƠŌÔÓŌŌ, byte(len([]rune(ŌΘƠÔÕÔΘÔOÔOŐΟΘŌΘƠÔÕÔΘÔOÔOŐΟΘ))))\n    }\n    return string(ΟŐÔÒÖÔΘÕÔŎÒÓÖÖÒΘ0ÖÔΟÖŎ0ÔOÓÖƠŌÔÓŌŌ)\n}\n```\n\nA slice of string is generated, with each element has a lenght derived from the byte value of the original char of the original string.\nThis way each byte of the original string is computed and calculated as the lenght of the correspondent string, casted to rune slice.\n\nThe launcher is compiled then using:\n\n```go\n    flags = []string{\"build\", \"-a\",\n        \"-trimpath\",\n        \"-gcflags\",\n        \"-N -l -nolocalimports\",\n        \"-ldflags\",\n        \"-s -w -extldflags -static\",\n    }\n    exec.Command(\"go\", flags...)\n```\n\nFile is the **stripped**, using `strip` with the flags:\n\n```bash\n    -sxX\n    --remove-section=.bss\n    --remove-section=.comment\n    --remove-section=.eh_frame\n    --remove-section=.eh_frame_hdr\n    --remove-section=.fini\n    --remove-section=.fini_array\n    --remove-section=.gnu.build.attributes\n    --remove-section=.gnu.hash\n    --remove-section=.gnu.version\n    --remove-section=.gosymtab\n    --remove-section=.got\n    --remove-section=.note.ABI-tag\n    --remove-section=.note.gnu.build-id\n    --remove-section=.note.go.buildid\n    --remove-section=.shstrtab\n    --remove-section=.typelink\n```\n\nAdditionally, if using *UPX*, their headers are **removed and replaced with randomness**, to ensure simple things like `upx -d` will not work.\n\nAdditionally a series of extra words are removed from the binary and replaced with random bytes, to make it harder to do static analysis:\n\n```\n    .gopclntab\n    .go.buildinfo\n    .noptrdata\n    .noptrbss\n    .data\n    .rodata\n    .text\n    .itablink\n    .shstrtab\n    .data\n    .dynamic\n    .dynstr\n    .dynsym\n    .gnu.version_r\n    .gopclntab\n    .got.plt\n    .init_array\n    .interp\n    .itablink\n    .rela.dyn\n    .rela.plt\n    .tbss\n    .plt\n    .init\n    name runtime command cmd\n    ptr process unicode main\n    path get reflect context\n    debug fmt sync sort\n    size heap fatal call\n    fixed slice bit file\n    read write buffer encrypt\n    decrypt hash state\n    external internal float\n    env trace pid\n```\n\nOutput of readelf to see the effect:\n\n![readelf](./pics/readelf.png)\n\n#### File Entropy\n\nUsing binwalk to analyze the file entropy can give some hint on how the process works:\n\nThis is the entropy of the binary we want to package (for this example /usr/bin/bash):\n\n![original-entropy.png](./pics/original-entropy.png)\n\nThis is the entropy of a packaged binary **without compression**\n\n![uncompressed](./pics/uncompressed-entropy.png)\n\nThis is the entropy of a packaged binary **with compression**\n\n![compressed](./pics/compressed-entropy.png)\n\nIn both cases (but mainly the first) it is possible to see when the launcher stops and\nthe garbage before the payload starts. This is really not a problem, because the offset of garbage is both pre-poned **and** post-poned to the payload, and the \"secret number\" of when it starts is kept inside the launcher and computed at runtime.\n\nThis is obviously vulnerable, reversing the binary will reveal the secret, all the launcher part is dedicated to the implementation of a series of measures to **block dynamic analysis** and try to force static analysis.\n\n## Part 2: the launcher\n\nThe launcher is the second part of the project, it allows to decompress, decrypt and launch the payload without touching storage, but using a file descriptor in RAM.\n\nThis is a well known technique as it is possible to read:\n\n- [In-Memory-Only ELF Execution (Without tmpfs) Mrs Quis Will Think of a Better Title](https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html)\n\n- [ELF in-memory execution](https://blog.fbkcs.ru/en/elf-in-memory-execution/)\n\nand in many other places in C programming literature.\n\n```\nPut briefly, use syscall to create a memory file descriptor (syscall 319 for amd64),\nwrite the plaintext payload here, and execute. \nThe fd will be automatically removed after the execution without \nleaving trace on the storage.\n```\n\n### Possible weakpoints\n\nThis approach is vulnerable to \n\n1. \"memory dump attack\", for example pausing the VM during execution and manually search the ram for all file descriptors until you find the right one\n\n2. \"proc dump attack\", in linux all the file descriptors are in `/proc` so dumping to another disk the complete folder will in a way or another dump the decrypted payload (if done before the execution finishes), **this is even more accentuated if stdout management is enabled**\n\n3. dynamic analysis, during execution \"pausing\" the process and spot the right fd\n\n4. reversing the binary to find the \"secret\" (which in our case is the offset) and from there, reverse the encryption process and reconstruct the plaintext\n\nFor point 1, it is possible to insert hypervisor detection, sandbox detection etc... it is in my TODO list, but I would leave it optional, in case you genuinely want to run the binary in VMs or Dockers.\n\nPoint 3 (and by consequence point 4) can be made harder by blocking dynamic analysis detecting debuggers, tracers and so on... \n\nForcing static analysis of the decompiled code is already a big step forward in protecting the binary execution.\n\n### Anti-debug\n\nImplemented here are a series of anti-debug techniques that are quite common in C/C++, from the **double-ptrace method** to the **ppid analysis** and breakpoints interception.\n\nFirst line of protection is breakpoints interception, on linux, a breakpoints is equivalent to signal *SIGILL* and *SIGTRAP* so:\n\n```go\n/*\nBreakpoint on linux are 0xCC and will be interpreted as a\nSIGTRAP, we will intercept them.\n*/\nfunc obSigTrap(obInput chan obOS.Signal) {\n    obMySignal := \u003c-obInput\n    switch obMySignal {\n    case obSyscall.SIGILL:\n        obExit()\n    case obSyscall.SIGTRAP:\n        obExit()\n    default:\n        return\n    }\n}\n```\n\nThis is pretty basic, so we go ahead and try to block **ptrace**:\n\n```go\n// attach to PTRACE, register if successful\n// attach A G A I N , register if unsuccessful\n// this protects against custom ptrace (always returning 0)\n// against NOP attacks and LD_PRELOAD attacks\nfunc obPtraceDetect() {\n\n\tvar obOffset = 0\n\n\tobProc, _ := obOS.FindProcess(obOS.Getppid())\n\n\tobErr := obSyscall.PtraceAttach(obProc.Pid)\n\tif obErr == nil {\n\t\tobOffset = 5\n\t}\n\n\tobErr = obSyscall.PtraceAttach(obProc.Pid)\n\tif obErr != nil {\n\t\tobOffset *= 3\n\t}\n\n\tif obOffset != (3 * 5) {\n\t\tobProc.Signal(obSyscall.SIGCONT)\n\t\tprintln(1)\n\n\t\treturn\n\t}\n\n\tobErr = obSyscall.PtraceDetach(obProc.Pid)\n\tif obErr != nil {\n\t\tobProc.Signal(obSyscall.SIGCONT)\n\t\tprintln(0)\n\n\t\treturn\n\t}\n\n\tobProc.Signal(obSyscall.SIGCONT)\n\tprintln(0)\n}\n```\n\n*Double ptraceme* ensures that tampering the ptrace loading with a fake ptrace lib that always returns 0, would result in a failure.\n\nCMD Line detection, would check for common processes for debugging, this is pretty naive check:\n\n```go\n/*\nCheck the process cmdline to spot if a debugger is inline\n*/\nfunc obParentCmdLineDetect() {\n    obPidParent := obOS.Getppid()\n\n    obNameFile := \"/proc/\" + obStrconv.FormatInt(int64(obPidParent), 10) +\n        \"/cmdline\"\n    obStatParent, _ := obUtilio.ReadFile(obNameFile)\n\n    if obStrings.Contains(string(obStatParent), \"gdb\") ||\n        obStrings.Contains(string(obStatParent), \"dlv\") ||\n        obStrings.Contains(string(obStatParent), \"edb\") ||\n        obStrings.Contains(string(obStatParent), \"frida\") ||\n        obStrings.Contains(string(obStatParent), \"ghidra\") ||\n        obStrings.Contains(string(obStatParent), \"godebug\") ||\n        obStrings.Contains(string(obStatParent), \"ida\") ||\n        obStrings.Contains(string(obStatParent), \"lldb\") ||\n        obStrings.Contains(string(obStatParent), \"ltrace\") ||\n        obStrings.Contains(string(obStatParent), \"strace\") ||\n        obStrings.Contains(string(obStatParent), \"valgrind\") {\n        obExit()\n    }\n}\n```\n\n```go\n/*\nCheck the process cmdline to spot if a debugger is the PPID of our process\n*/\nfunc obParentDetect() {\n    obPidParent := obOS.Getppid()\n\n    obNameFile := \"/proc/\" + obStrconv.FormatInt(int64(obPidParent), 10) +\n        \"/stat\"\n    obStatParent, _ := obUtilio.ReadFile(obNameFile)\n\n    if obStrings.Contains(string(obStatParent), \"gdb\") ||\n        obStrings.Contains(string(obStatParent), \"dlv\") ||\n        obStrings.Contains(string(obStatParent), \"edb\") ||\n        obStrings.Contains(string(obStatParent), \"frida\") ||\n        obStrings.Contains(string(obStatParent), \"ghidra\") ||\n        obStrings.Contains(string(obStatParent), \"godebug\") ||\n        obStrings.Contains(string(obStatParent), \"ida\") ||\n        obStrings.Contains(string(obStatParent), \"lldb\") ||\n        obStrings.Contains(string(obStatParent), \"ltrace\") ||\n        obStrings.Contains(string(obStatParent), \"strace\") ||\n        obStrings.Contains(string(obStatParent), \"valgrind\") {\n        obExit()\n    }\n}\n```\n\nthis goes in conjunction with the TracePid check to see if a parent is tracing us:\n\n```go\n/*\nCheck the process status to spot if a debugger is active using the TracePid key\n*/\nfunc obParentTracerDetect() {\n    obPidParent := obOS.Getppid()\n\n    obNameFile := \"/proc/\" + obStrconv.FormatInt(int64(obPidParent), 10) +\n        \"/status\"\n    obStatParent, _ := obUtilio.ReadFile(obNameFile)\n    obStatLines := obStrings.Split(string(obStatParent), \"\\n\")\n\n    for _, obValue := range obStatLines {\n        if obStrings.Contains(obValue, \"TracerPid\") {\n            obSplitArray := obStrings.Split(obValue, \":\")\n            obSplitValue := obStrings.Replace(obSplitArray[1], \"\\t\", \"\", -1)\n\n            if obSplitValue != \"0\" {\n                obExit()\n            }\n        }\n    }\n}\n```\n\nand verification that the process cmdline corresponds to \"argv[0]\" (for example, launching `strace mybin arg1` would result in a cmdline of `strace` and argv[0] of `mybin`\n\n```go\n/*\nCheck the process cmdline to spot if a debugger is launcher\n\"_\" and Args[0] should match otherwise\n*/\nfunc obEnvArgsDetect() {\n    obLines, _ := obOS.LookupEnv(\"_\")\n    if obLines != obOS.Args[0] {\n        obExit()\n    }\n}\n```\n\nand check if there is none of the known debuggers inline with the command\n\n```go\n/*\nCheck the process cmdline to spot if a debugger is inline\n\"_\" should not contain the name of any debugger\n*/\nfunc obEnvParentDetect() {\n    obLines, _ := obOS.LookupEnv(\"_\")\n    if obStrings.Contains(obLines, \"gdb\") ||\n        obStrings.Contains(obLines, \"dlv\") ||\n        obStrings.Contains(obLines, \"edb\") ||\n        obStrings.Contains(obLines, \"frida\") ||\n        obStrings.Contains(obLines, \"ghidra\") ||\n        obStrings.Contains(obLines, \"godebug\") ||\n        obStrings.Contains(obLines, \"ida\") ||\n        obStrings.Contains(obLines, \"lldb\") ||\n        obStrings.Contains(obLines, \"ltrace\") ||\n        obStrings.Contains(obLines, \"strace\") ||\n        obStrings.Contains(obLines, \"valgrind\") {\n        obExit()\n    }\n}\n```\n\nAlso a pretty common check to do if we are in a debugger is to see if ENV has the variables LINES and COLUMNS:\n\n```go\n/*\nCheck the process cmdline to spot if a debugger is active\nmost debuggers (like GDB) will set LINE,COLUMNS or LD_PRELOAD\nto function, we try to spot this\n*/\nfunc obEnvDetect() {\n    _, obLines := obOS.LookupEnv(\"LINES\")\n    _, obColumns := obOS.LookupEnv(\"COLUMNS\")\n    _, obLineLdPreload := obOS.LookupEnv(\"LD_PRELOAD\")\n\n    if obLines || obColumns || obLineLdPreload {\n        obExit()\n    }\n}\n```\n\nThis can give false positives (example the embedded terminal in VSCode or VIM), but worth checking for a normal environment.\n\nAlso banally checking for LD_PRELOAD env variable present:\n\n```go\n/*\nCheck the process is launcher with a LD_PRELOAD set.\nThis can be an injection attack (like on frida) to try and circumvent\nvarious restrictions (like ptrace checks)\n*/\nfunc obLdPreloadDetect() {\n    obKey := obStrconv.FormatInt(obTime.Now().UnixNano(), 10)\n    obValue := obStrconv.FormatInt(obTime.Now().UnixNano(), 10)\n\n    err := obOS.Setenv(obKey, obValue)\n    if err != nil {\n        obExit()\n    }\n\n    obLineLdPreload, _ := obOS.LookupEnv(obKey)\n    if obLineLdPreload == obValue {\n        err := obOS.Unsetenv(obKey)\n        if err != nil {\n            obExit()\n        }\n    } else {\n        obExit()\n    }\n}\n```\n\nto make it more resilient to \"false environment\" attacks, we also try and set a random key-value in the environment, and check if it works, to ensure we do not have a \"fake\" environment (always empty for example).\n\nThis type of checks are pretty basic and easy to port from C to Go. \n\nA couple of checks I would like to port are for example the heap relocation check, as explained in this repo: [debugmenot/test_nearheap.c at master · kirschju/debugmenot · GitHub](https://github.com/kirschju/debugmenot/blob/master/src/test_nearheap.c) \n\n\u003e  GDB relocates the heap to the end of the bss section\n\nThis type of check is not easily done in Go because *go does not support pointer arithmetic*, CGO should be the way, but would make it dynamically linked for the C part (or twice the size if statically linked)\n\n### Making difficult to reverse\n\nTo add to this in many points of the source code it is possible to see that a comment is made:\n\n`//OB_CHECK`\n\nThis is because during the obfuscation phase, every time we encounter this string, we will inject a random permutation of ALL of the anti-debug tricks that will be executed in parallel using go routines.\n\n```go\n/*\nGenerateRandomAntiDebug will Insert random order of anti-debug check\ntogether with inline compilation to induce big number\nof instructions in random order\n*/\nfunc GenerateRandomAntiDebug(input string) string {\n    lines := strings.Split(input, \"\\n\")\n    randomChecks := []string{\n        `obDependencyCheck()`,\n        `obEnvArgsDetect()`,\n        `obParentTracerDetect()`,\n        `obParentCmdLineDetect()`,\n        `obEnvDetect()`,\n        `obEnvParentDetect() `,\n        `obLdPreloadDetect()`,\n        `obParentDetect()`,\n    }\n    // find OB_CHECK and put the checks there.\n    for i, v := range lines {\n        if strings.Contains(v, \"// OB_CHECK\") {\n            threadString := \"\"\n            checkString := \"\"\n            // randomize order of check to replace\n            for j, v := range ShuffleSlice(randomChecks) {\n                threadString = threadString + \"go \" + v + \";\"\n                checkString += v\n\n                if j != (len(randomChecks) - 1) {\n                    checkString += `||`\n                }\n            }\n            // add action in case of failed check\n            lines[i] = threadString\n        }\n    }\n    // back to single string\n    return strings.Join(lines, \"\\n\")\n}\n```\n\nThe generated source will be filled with this always-changing code, that will make difficult to use NOP attacks, manual jump using breakpoints and make confusion in the  graph view of disassemblers like Cutter, IDA Pro or Ghidra.\n\nSo the main, for example, becomes something like:\n\n```go\nfunc main() {\n    // Prepare to intercept SIGTRAP\n    ÖÖΘÓOŐƠOΟŌΟŐÔÒÕÓÒÒÒOŎŌO0ÔŌÓÖOŐŎΟŌŌŌÔ0OOŌŐΘŎΘÔOOOÓΘŐΘ00ƠŎÖÖ0ÔÕÕŎŎŎÒÖO00ŎÔΘÓOÖ0ΘÔOΟÒŐŐƠÒΘÒ0ŎŌŌÖΟÓ0ƠŎŌŌΟŎΘŐÖÒŐΘÔÕOŌÕŎÒŎÔŌÓƠÓŌŐΟΟÒÒƠ := make(chan ÒÕ0ΟÓŎ0ÖÒŌƠÒΘ0ÓΘΘƠΟÕƠΘÕO0OŐOŎOƠŎƠÖOOÒÖÒO0ÕÕÓÖΘΘÓÒÕŌŐΘŐƠÖOÕΘÔÕÖƠƠŌŐƠ0ÖÖƠŌΟ0ΘƠÕÒÔƠÒOÒΘÔΟÒ0OÕOŌÕΘÒÒŐÓΟÕÖÕŎΟƠŎOŌÔΟŎŐÒŎŎOƠOO0Ō0ŌŐÓÖŎÖ.Signal, 1)\n    ÔƠÕΟÒΘΘÖŎ0ÓŎÒŎOÕΟÒ0ŐΘƠÓOƠOÔÒÕŐ0OÔÒÓΟOÓŐŐŎΟÒŐÒ0ŌΟO0ΟOÔOΘOƠƠÒ0ŐÓΘÖΘΘÕÖΟÔÖÖΘÔŎÕ00ŐÖÔŎŎŌ0ŐƠOÖÖΟΘÕÓŌÖÔÖÕÒƠƠÓÓ0ÒÖƠŌÔÔŐƠŌOŐÔÒ0ŐÕÖOŐŌŎÔÔ.Notify(ÖÖΘÓOŐƠOΟŌΟŐÔÒÕÓÒÒÒOŎŌO0ÔŌÓÖOŐŎΟŌŌŌÔ0OOŌŐΘŎΘÔOOOÓΘŐΘ00ƠŎÖÖ0ÔÕÕŎŎŎÒÖO00ŎÔΘÓOÖ0ΘÔOΟÒŐŐƠÒΘÒ0ŎŌŌÖΟÓ0ƠŎŌŌΟŎΘŐÖÒŐΘÔÕOŌÕŎÒŎÔŌÓƠÓŌŐΟΟÒÒƠ, O0OÓΟ0ŎŐOƠOŎƠ0ÓƠΘÒƠÕƠÖÓÕŎƠŎOΟΘƠŌƠÖÔÓÔÕŐŎŌŐŐƠΘO0Ô0Ò0OÕƠ0ŐÓÖÔ0ŎΘÕÔÔŎƠÓÔOÕO0ÓΘÒOŎŎÔ0ÓƠ0ÖŎŎΘO0ƠŐÖŐŌOOOΟOÒΘΟΟƠΘΟ0ÖÔŎƠÓŌÕÕOΘÕŎŐŌΘOŐΘÕŌ.SIGTRAP, O0OÓΟ0ŎŐOƠOŎƠ0ÓƠΘÒƠÕƠÖÓÕŎƠŎOΟΘƠŌƠÖÔÓÔÕŐŎŌŐŐƠΘO0Ô0Ò0OÕƠ0ŐÓÖÔ0ŎΘÕÔÔŎƠÓÔOÕO0ÓΘÒOŎŎÔ0ÓƠ0ÖŎŎΘO0ƠŐÖŐŌOOOΟOÒΘΟΟƠΘΟ0ÖÔŎƠÓŌÕÕOΘÕŎŐŌΘOŐΘÕŌ.SIGILL)\n\n    go ÔΘƠƠƠŎÔŐŌÖOŐÒÓO0ÖΘΘ0ΘŐŎΘ0ÖŌOÒŐÕ0ÒŐ0ƠÖÕŐÓÕÕŌƠƠÖÒÔŎÔÔÖÕŐÖΟÒÖŐÓÕÔÓΘΟÕƠOŐ0ŌÓÖÔOƠÓ0ŎÓ0ŎOÒΟÓOΘÔΟÒÔƠΘŐÒΘΘÓƠΟƠÓÒÓƠ0Ŏ0ÓÒŌOŌÕÕΟŌOΘ0ŎƠŎ0ÕƠƠ(ÖÖΘÓOŐƠOΟŌΟŐÔÒÕÓÒÒÒOŎŌO0ÔŌÓÖOŐŎΟŌŌŌÔ0OOŌŐΘŎΘÔOOOÓΘŐΘ00ƠŎÖÖ0ÔÕÕŎŎŎÒÖO00ŎÔΘÓOÖ0ΘÔOΟÒŐŐƠÒΘÒ0ŎŌŌÖΟÓ0ƠŎŌŌΟŎΘŐÖÒŐΘÔÕOŌÕŎÒŎÔŌÓƠÓŌŐΟΟÒÒƠ)\n\n    // ÔOÒŎ00ƠÒŎΟOÓΟÖΟOÖΘÓƠƠƠŎÖŌŐŐÒÓÔOŎÒÔΘŌΟÓŐÒΟƠOÕÖÕƠƠÕŎÔÔÒΟOÖO0ŐÔÓÓƠÒ0ŎΘOÒŎOŐÔŌƠÓÒÕÕΟO0ÓƠÒŎŌÖOŌÖOÕÒƠŐÓΟŐÔÔŎŐÓÔÓOƠΘOOŎ0ƠΘÓΘÕÔÕƠÓΘÒÔŐΟÒ()\n    go ÔŐŌŌÒÓ0OΟO0ÖΟŌ0ƠÒ0ÒŌÔOΟΟÖŎÕ0ΘÓΘOOΟΟΘ0Ơ0ΘŐÕ0ΟÔŐ0ŐŌÕ00ŌOΟΟÒÕÒÖŌÓOOÒÔΘÒΘÒÔŌŎΟÒŐŌÕÒÕŎÖÒÖÕΘ00OÖÓŌÓÖÖÓÔƠΘŌ0ÓÕ0ŌÖΘOÖ0ÓŐƠ0ΟÖ00ŌŐŐΘΘÕÒƠÕΟ()\n    go ÖƠŎŎΘΟÓÓÒÖÒ0ÓΘŎÖΘŌ0ŎOÕÔÓƠÔÔΘŌÔΘ0OÕOΟOÕƠŌÒŎ0O0ŎΘΟÒÕΟΟÓ0ŎΟÒΘÕΟÖŎÕÔOÕOΘΘŐÖŌŐΟÔΟÖΟ0OÔÓ0ΟŐOŌÕŌOŌOÕÒÒΘΟŎƠΟÒŌ0ƠΟÔOÒÓÒΟŐÕÒŌƠÔÖ0ŐÖÖÖÓΟƠŌÕ()\n    go ƠΘÔŐŎŌÒŌÖŌΟŎOΟŐŎŎΘOƠÔ0ŐƠΘÒΟƠŐŌ0ŌΘΟΟÒŎÖÓ0ÒΟŐ0ƠΘŌÒÔÔŌƠŌÖOƠÓÔŌOÓ0OŐÕÒ0ŌÖÓŎÕŎÕÓÓ0ÒΘÖΟΘŎÕÓƠŐΟÕΟŌÔŎŌÓOÒÓ0Ŏ0ΟÕÔÒŌƠΘŌΟŎŐΘƠÔÓŐΘŎÔŌŌO0ƠÒÖÖ()\n    go ΟƠŐΟÔOƠÔΟΟÓΘÖOŐΟÕƠ0ΘŐÓÖÔŎÒ0OÔŐÓΟƠΘΘΘÔOÖŌÕŎÒŎΟÔÕÒŎÓ0OÖƠ0ÖÓOÒŐÓŐÔΘÓŌƠÕŎÕÒΟÖŌÒŎΟ0ÒÔÔÕ0ÕÓÒÓÒÔ0ŎO0Θ0ΘŐÖΟŎÖŌOΘÒΘ0ÕŐÖÓΟOÖÒÒÔÕOƠÒŎΘO0ÔÓÔ()\n    go ÒŎ0ÖƠÔŌÔÖΟΘƠΟÒƠƠÖ0ŐÓÓΟOÓŎÖΟÔŌOOΟÔÒÓƠÓΟŎŎ0ÕƠŌÕÒÒÖ0OŌÕŐ0ƠÖΟƠÕÔÒÖŎÖÔÕÔŐOƠŌƠÒΘŌŎÖOŎΘÓÔÔΟŐŌΟŐƠÕΟÖŐ0ÔΘƠŌƠƠÒOOŎÔ0ŐΟÕÕŌÕÔÕÒŐÔŐÔÖÓƠÖΟOŌŎƠ()\n    go ÔŌÕŌŐ0ŐÓÖΘŐÖΟŎΟƠŎOÖÖÕÒΘƠŐΟÓΟΘOÒƠΟ0ÒÒÖÖŌŎŌΘƠƠOΟÒƠΟΘOÓÕŌÕÒÕO0ŎOÔŐOÖŎÓÔOO0OÕŌOΘ0ÒƠƠÔŌÔÔÔŐΟ0ΘΘOŌOÒÕÒÕÕOÔÕŐŌΟÒŌÔ00ÖƠÔΟ0ΟÔŐΟƠÖÕΘ0ÖΟŌÖÖ()\n    go ÒŎÒ0ΘŐŐÔƠΘÒŐŎΘÓÕÕÔŎÖ0ŌÖÖΟÖÓÔÓÒÖÖŌŐΘŌ0ÕÖŎÓΟŌŌÒÖ0ΘÖOΘŎŎÖΘΟÓÒƠΟŐOƠOΘΘÓ0ÕΘŐƠÔÖΟΟ0OΘÖΘŎΟŐÕΘŐΟÓÓƠƠŐÒΟÖΘOŌƠΟΘŎ0ÒΟƠƠÔŎ00ŌÕÓÒ0ŌŎÒOƠŎÖŐÕÓƠ()\n    go ŎÓÕƠŌOŌΟÖÓ0ÒÓΘΟ0ƠŎÖΟŌŎÕÒÕÓŎÖÕƠÒŎŐƠÒƠ0ÒƠÖÕŎÒŐÓÔÓÒ0ΟÕŌÒŐOŐŌÖOÕƠƠÔŎÖ0ÒÓÒÕOÖÕƠÔÕOÔÖΟΘÕÔ0OÕƠÓOÓOÔÖÒ0ΟÖOÖÔΘŌOŌŎΘΘΘ0ŐŌOÔÒΟƠ0ŐÓÕŐÔŐΟÒŐÒΟ()\n    ΟƠŐΟÔOƠÔΟΟÓΘÖOŐΟÕƠ0ΘŐÓÖÔŎÒ0OÔŐÓΟƠΘΘΘÔOÖŌÕŎÒŎΟÔÕÒŎÓ0OÖƠ0ÖÓOÒŐÓŐÔΘÓŌƠÕŎÕÒΟÖŌÒŎΟ0ÒÔÔÕ0ÕÓÒÓÒÔ0ŎO0Θ0ΘŐÖΟŎÖŌOΘÒΘ0ÕŐÖÓΟOÖÒÒÔÕOƠÒŎΘO0ÔÓÔ()\n    go ÔŐŌŌÒÓ0OΟO0ÖΟŌ0ƠÒ0ÒŌÔOΟΟÖŎÕ0ΘÓΘOOΟΟΘ0Ơ0ΘŐÕ0ΟÔŐ0ŐŌÕ00ŌOΟΟÒÕÒÖŌÓOOÒÔΘÒΘÒÔŌŎΟÒŐŌÕÒÕŎÖÒÖÕΘ00OÖÓŌÓÖÖÓÔƠΘŌ0ÓÕ0ŌÖΘOÖ0ÓŐƠ0ΟÖ00ŌŐŐΘΘÕÒƠÕΟ()\n    go ƠΘÔŐŎŌÒŌÖŌΟŎOΟŐŎŎΘOƠÔ0ŐƠΘÒΟƠŐŌ0ŌΘΟΟÒŎÖÓ0ÒΟŐ0ƠΘŌÒÔÔŌƠŌÖOƠÓÔŌOÓ0OŐÕÒ0ŌÖÓŎÕŎÕÓÓ0ÒΘÖΟΘŎÕÓƠŐΟÕΟŌÔŎŌÓOÒÓ0Ŏ0ΟÕÔÒŌƠΘŌΟŎŐΘƠÔÓŐΘŎÔŌŌO0ƠÒÖÖ()\n    go ÖƠŎŎΘΟÓÓÒÖÒ0ÓΘŎÖΘŌ0ŎOÕÔÓƠÔÔΘŌÔΘ0OÕOΟOÕƠŌÒŎ0O0ŎΘΟÒÕΟΟÓ0ŎΟÒΘÕΟÖŎÕÔOÕOΘΘŐÖŌŐΟÔΟÖΟ0OÔÓ0ΟŐOŌÕŌOŌOÕÒÒΘΟŎƠΟÒŌ0ƠΟÔOÒÓÒΟŐÕÒŌƠÔÖ0ŐÖÖÖÓΟƠŌÕ()\n    go ÒŎÒ0ΘŐŐÔƠΘÒŐŎΘÓÕÕÔŎÖ0ŌÖÖΟÖÓÔÓÒÖÖŌŐΘŌ0ÕÖŎÓΟŌŌÒÖ0ΘÖOΘŎŎÖΘΟÓÒƠΟŐOƠOΘΘÓ0ÕΘŐƠÔÖΟΟ0OΘÖΘŎΟŐÕΘŐΟÓÓƠƠŐÒΟÖΘOŌƠΟΘŎ0ÒΟƠƠÔŎ00ŌÕÓÒ0ŌŎÒOƠŎÖŐÕÓƠ()\n    go ΟƠŐΟÔOƠÔΟΟÓΘÖOŐΟÕƠ0ΘŐÓÖÔŎÒ0OÔŐÓΟƠΘΘΘÔOÖŌÕŎÒŎΟÔÕÒŎÓ0OÖƠ0ÖÓOÒŐÓŐÔΘÓŌƠÕŎÕÒΟÖŌÒŎΟ0ÒÔÔÕ0ÕÓÒÓÒÔ0ŎO0Θ0ΘŐÖΟŎÖŌOΘÒΘ0ÕŐÖÓΟOÖÒÒÔÕOƠÒŎΘO0ÔÓÔ()\n    go ŎÓÕƠŌOŌΟÖÓ0ÒÓΘΟ0ƠŎÖΟŌŎÕÒÕÓŎÖÕƠÒŎŐƠÒƠ0ÒƠÖÕŎÒŐÓÔÓÒ0ΟÕŌÒŐOŐŌÖOÕƠƠÔŎÖ0ÒÓÒÕOÖÕƠÔÕOÔÖΟΘÕÔ0OÕƠÓOÓOÔÖÒ0ΟÖOÖÔΘŌOŌŎΘΘΘ0ŐŌOÔÒΟƠ0ŐÓÕŐÔŐΟÒŐÒΟ()\n    go ÔŌÕŌŐ0ŐÓÖΘŐÖΟŎΟƠŎOÖÖÕÒΘƠŐΟÓΟΘOÒƠΟ0ÒÒÖÖŌŎŌΘƠƠOΟÒƠΟΘOÓÕŌÕÒÕO0ŎOÔŐOÖŎÓÔOO0OÕŌOΘ0ÒƠƠÔŌÔÔÔŐΟ0ΘΘOŌOÒÕÒÕÕOÔÕŐŌΟÒŌÔ00ÖƠÔΟ0ΟÔŐΟƠÖÕΘ0ÖΟŌÖÖ()\n    go ÒŎ0ÖƠÔŌÔÖΟΘƠΟÒƠƠÖ0ŐÓÓΟOÓŎÖΟÔŌOOΟÔÒÓƠÓΟŎŎ0ÕƠŌÕÒÒÖ0OŌÕŐ0ƠÖΟƠÕÔÒÖŎÖÔÕÔŐOƠŌƠÒΘŌŎÖOŎΘÓÔÔΟŐŌΟŐƠÕΟÖŐ0ÔΘƠŌƠƠÒOOŎÔ0ŐΟÕÕŌÕÔÕÒŐÔŐÔÖÓƠÖΟOŌŎƠ()\n    ÒŎ0ÖƠÔŌÔÖΟΘƠΟÒƠƠÖ0ŐÓÓΟOÓŎÖΟÔŌOOΟÔÒÓƠÓΟŎŎ0ÕƠŌÕÒÒÖ0OŌÕŐ0ƠÖΟƠÕÔÒÖŎÖÔÕÔŐOƠŌƠÒΘŌŎÖOŎΘÓÔÔΟŐŌΟŐƠÕΟÖŐ0ÔΘƠŌƠƠÒOOŎÔ0ŐΟÕÕŌÕÔÕÒŐÔŐÔÖÓƠÖΟOŌŎƠ()\n    go ÒŎ0ÖƠÔŌÔÖΟΘƠΟÒƠƠÖ0ŐÓÓΟOÓŎÖΟÔŌOOΟÔÒÓƠÓΟŎŎ0ÕƠŌÕÒÒÖ0OŌÕŐ0ƠÖΟƠÕÔÒÖŎÖÔÕÔŐOƠŌƠÒΘŌŎÖOŎΘÓÔÔΟŐŌΟŐƠÕΟÖŐ0ÔΘƠŌƠƠÒOOŎÔ0ŐΟÕÕŌÕÔÕÒŐÔŐÔÖÓƠÖΟOŌŎƠ()\n    go ÔŌÕŌŐ0ŐÓÖΘŐÖΟŎΟƠŎOÖÖÕÒΘƠŐΟÓΟΘOÒƠΟ0ÒÒÖÖŌŎŌΘƠƠOΟÒƠΟΘOÓÕŌÕÒÕO0ŎOÔŐOÖŎÓÔOO0OÕŌOΘ0ÒƠƠÔŌÔÔÔŐΟ0ΘΘOŌOÒÕÒÕÕOÔÕŐŌΟÒŌÔ00ÖƠÔΟ0ΟÔŐΟƠÖÕΘ0ÖΟŌÖÖ()\n    go ƠΘÔŐŎŌÒŌÖŌΟŎOΟŐŎŎΘOƠÔ0ŐƠΘÒΟƠŐŌ0ŌΘΟΟÒŎÖÓ0ÒΟŐ0ƠΘŌÒÔÔŌƠŌÖOƠÓÔŌOÓ0OŐÕÒ0ŌÖÓŎÕŎÕÓÓ0ÒΘÖΟΘŎÕÓƠŐΟÕΟŌÔŎŌÓOÒÓ0Ŏ0ΟÕÔÒŌƠΘŌΟŎŐΘƠÔÓŐΘŎÔŌŌO0ƠÒÖÖ()\n    go ÒŎÒ0ΘŐŐÔƠΘÒŐŎΘÓÕÕÔŎÖ0ŌÖÖΟÖÓÔÓÒÖÖŌŐΘŌ0ÕÖŎÓΟŌŌÒÖ0ΘÖOΘŎŎÖΘΟÓÒƠΟŐOƠOΘΘÓ0ÕΘŐƠÔÖΟΟ0OΘÖΘŎΟŐÕΘŐΟÓÓƠƠŐÒΟÖΘOŌƠΟΘŎ0ÒΟƠƠÔŎ00ŌÕÓÒ0ŌŎÒOƠŎÖŐÕÓƠ()\n    go ΟƠŐΟÔOƠÔΟΟÓΘÖOŐΟÕƠ0ΘŐÓÖÔŎÒ0OÔŐÓΟƠΘΘΘÔOÖŌÕŎÒŎΟÔÕÒŎÓ0OÖƠ0ÖÓOÒŐÓŐÔΘÓŌƠÕŎÕÒΟÖŌÒŎΟ0ÒÔÔÕ0ÕÓÒÓÒÔ0ŎO0Θ0ΘŐÖΟŎÖŌOΘÒΘ0ÕŐÖÓΟOÖÒÒÔÕOƠÒŎΘO0ÔÓÔ()\n    go ÔŐŌŌÒÓ0OΟO0ÖΟŌ0ƠÒ0ÒŌÔOΟΟÖŎÕ0ΘÓΘOOΟΟΘ0Ơ0ΘŐÕ0ΟÔŐ0ŐŌÕ00ŌOΟΟÒÕÒÖŌÓOOÒÔΘÒΘÒÔŌŎΟÒŐŌÕÒÕŎÖÒÖÕΘ00OÖÓŌÓÖÖÓÔƠΘŌ0ÓÕ0ŌÖΘOÖ0ÓŐƠ0ΟÖ00ŌŐŐΘΘÕÒƠÕΟ()\n    go ŎÓÕƠŌOŌΟÖÓ0ÒÓΘΟ0ƠŎÖΟŌŎÕÒÕÓŎÖÕƠÒŎŐƠÒƠ0ÒƠÖÕŎÒŐÓÔÓÒ0ΟÕŌÒŐOŐŌÖOÕƠƠÔŎÖ0ÒÓÒÕOÖÕƠÔÕOÔÖΟΘÕÔ0OÕƠÓOÓOÔÖÒ0ΟÖOÖÔΘŌOŌŎΘΘΘ0ŐŌOÔÒΟƠ0ŐÓÕŐÔŐΟÒŐÒΟ()\n    go ÖƠŎŎΘΟÓÓÒÖÒ0ÓΘŎÖΘŌ0ŎOÕÔÓƠÔÔΘŌÔΘ0OÕOΟOÕƠŌÒŎ0O0ŎΘΟÒÕΟΟÓ0ŎΟÒΘÕΟÖŎÕÔOÕOΘΘŐÖŌŐΟÔΟÖΟ0OÔÓ0ΟŐOŌÕŌOŌOÕÒÒΘΟŎƠΟÒŌ0ƠΟÔOÒÓÒΟŐÕÒŌƠÔÖ0ŐÖÖÖÓΟƠŌÕ()\n\n\n.....\n```\n\n### \n\n### Dependency Registration\n\nAnother form of protection, is the *dependency registration*.\n\nThe idea behind it is to protect not only elf binaries, but also executable scripts, like python, bash, perl, php or anything with a shebang.\n\nA common attack that is possible is a \"man in the middle execution\", for example, let the payload be a bash script, in the moment of execution `/usr/bin/env bash` is called to execute the content of the script.\n\nIf in the environment (or really with a symlink on /usr/bin/bash) we put something like:\n\n```bash\n#!/bin/sh\n\ncp $0 /tmp/plaintext\n\n/usr/bin/real-bash $@\n```\n\nThis will result in a transparent execution but a dumped plaintext in /tmp\n\n(credits for the attack idea to [mrnfrancesco (Francesco Marano) · GitHub](https://github.com/mrnfrancesco) )\n\n### \n\nSo what can we do to ensure nothing like this happens?\n\nWe will *register a binary/dependency* that is necessary for the payload to run (for example /usr/bin/bash for a bash script, /usr/bin/python3 for a python script etc etc) and verify that the dependency in the target where we would execute the payload is valid or not.\n\nNow there is a problem, how do we verify that it's a valid binary? \n\nUsing an Hash is too strict , for  example /usr/bin/bash on fedora is different from the one on centos 8, but both are valid binaries.\n\nOnly the presence of it is not enough (like in the example attack before)\n\nSome basic checks are made:\n\n- the file exists\n\n- file size is +/- 15% of the registered one\n\nBut something more is needed.\n\n#### Byte Frequency Distribution Study\n\nTo address the problem it is possible to recycle a technique mostly used in the data-recovery territory: the \u003cu\u003ebyte frequency distribution study\u003c/u\u003e.\n\nReferences:\n\n- [byte frequency analysis descriptor with spatial information for file fragment classification| Semantic Scholar](https://www.semanticscholar.org/paper/BYTE-FREQUENCY-ANALYSIS-DESCRIPTOR-WITH-SPATIAL-FOR-Xie-Abdullah/c39872eae0c61ecf47603aab3f5c1545ee612ac9)\n\n- [A New Approach to Content-based File Type Detection](https://arxiv.org/pdf/1002.3174)\n\nThe idea is to register the BFD of our dependency, then calculate the correlation index of Bravais-Pearson to see if the two dataset are linearly correlated.\n\n\u003e Ref: [Pearson correlation coefficient - Wikipedia](https://en.wikipedia.org/wiki/Pearson_correlation_coefficient)\n\nPearson's correlation coefficient is the [covariance](https://en.wikipedia.org/wiki/Covariance \"Covariance\") of the two variables divided by the product of their [standard deviations](https://en.wikipedia.org/wiki/Standard_deviations \"Standard deviations\").\n\n![pearson](https://wikimedia.org/api/rest_v1/media/math/render/svg/f76ccfa7c2ed7f5b085115086107bbe25d329cec)\n\nIf the index indicates strong correlation, we know that the two file are of the same type (binary)\n\nA second step is to study the combined standard deviation of the two datasets (std deviation of the first and the second) and see if both values, the correlation and the combined std deviation are in certain ranges.\n\nThis ensures that the dependency we are finding is of *the same type* (a binary) using the Bravais-Pearson index and *similar distribution* from the combined std deviation.\n\nHere an example of a valid dependency: /usr/bin/bash on fedora30 vs centos7\n\n![bfd](./pics/bfd-std.png)\n\nBlue and green the two BFD of the binaries, in green the combined std deviation.\n\nThis method is able to distinguish (and stop) the use of different (but functionally equal) binaries like zsh, dash or busybox\n\n### Decryption\n\nThe last line of defense is also the encryption of the payload. \n\nThe decryption key of the payload is the sha512sum of the compiled launcher itself with the random garbage appended to it.\n\nFor example:\n\nWith offset 930000\n\n```\nCompiled launcher (800kb)\n\nOFFSET1 (130kb)\n\nPayload\n\nOFFSET2 ( different one, 130kb)\n```\n\nThe decryption key is \n\n`sha512sum(Launcher+OFFSET1)`\n\nThis protects from file-based NOP attacks to remove some instructions from the launcher, and acts also as binary validation.\n\n![payload](./pics/decryption.png)\n\nThe payload is structured as above, OFFSET1 **ends on the offset value**, OFFSET2 is calculated as the byte reverse of the offset value.\n\nThe procedure will:\n\n- calculate the offset\n\n- take the launcher+OFFSET1, sha512sum it\n\n- remove OFFSET2 random bytes at the end\n\n- decrypt payload using the calculated sha512sum\n\nSo **THE REAL DECRYPTION KEY IS BASED ON THE OFFSET ITSELF**, all the obfuscation/anti-debug is to protect this information that is stored in an obfuscated string (that is not saved but computed at runtime) of random name and content. \n\n### Execution\n\nAs explained above, we will use a memory file descriptor to execute the binary without passing for the storage.\n\nThe binary will be executed using the `Command` library of Go, that uses the syscall exec under the hood, so no new shells are instantiated.\n\n### **If stdout management is NOT enabled (default)**\n\nThe process is launched and disowned.\nUpon the termination of the launcher process, the `/proc/PID/fd/mem` of the process is deleted, so the only \"copy\" of the payload is in the process table of the detached process itself\n\n**This is the most secure approach** as it deletes all instances of the plaintext payload, leaving it only in a highly private part of the RAM of the process itself, even gaining immunity to /proc dump attacks\n\nThis way even a script launcher with this approach is not possible to be retrieved:\n\n![stdout](./pics/handle-stdout.png)\n\n### **If stdout management is enabled**\n\nTwo routines are used to pipe the payload stderr and stdout to the launcher process.\n\nAlso piping is supported.\n\nFor IO heavy processes it is possible to insert in the `Scan` of the outputs an `OB_CHECK` like this:\n\n```go\n    // OB_CHECK\n    // async fetch stdout\n    go func() {\n        defer obWaitGroup.Done()\n\n        for obStdoutScan.Scan() {\n            // OB_CHECK\n            println(obStdoutScan.Text())\n        }\n    }()\n    // OB_CHECK\n    // async fetch stderr\n    go func() {\n        defer obWaitGroup.Done()\n\n        for obStderrScan.Scan() {\n            // OB_CHECK\n            println(obStderrScan.Text())\n        }\n    }()\n```\n\nThis will make an impact on performance (all check are executed **for each IO on any standard output/error**)  but can give a layer of hardness to process hijacking or tracing\n\n\u003cu\u003e*\u003cmark\u003e**this is the least secure approach**\u003c/mark\u003e*\u003c/u\u003e as while the launcher is running the `/proc/PID/fd/mem` of the process is still accessible and thus containing the plaintext payload ready to be stealed\n\nThis is particularly discouraged for **srcipts payloads**, as they are even easier to spot as the path is directly in the process\n\n![handle](./pics/handle-stdout-2.png)\n\nWith binaries the approach is safer (only the binary name is in the process, thus making it hard to spot requiring a complete /proc dump)\n\n\n## License\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2F89luca89%2Fpakkero.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2F89luca89%2Fpakkero?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F89luca89%2Fpakkero","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F89luca89%2Fpakkero","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F89luca89%2Fpakkero/lists"}