{"id":18282593,"url":"https://github.com/rafael-santiago/aegis","last_synced_at":"2025-04-05T06:30:47.864Z","repository":{"id":80005521,"uuid":"315821204","full_name":"rafael-santiago/aegis","owner":"rafael-santiago","description":"Linux, FreeBSD, NetBSD, OpenBSD and Windows debugging detection library. With support for C and Go.","archived":false,"fork":false,"pushed_at":"2021-11-18T12:35:14.000Z","size":428,"stargazers_count":42,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-20T23:41:41.446Z","etag":null,"topics":["anti-debug","cgo","cgo-bindings","cross-platform","debugging-facility","golang","golang-package","information-security","infosec","linux-hacking-tools","linux-library","minimalistic","procfs","ptrace","software-protection","suckless","system-hacking","system-programming","unix","unix-extension"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-4-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rafael-santiago.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-25T03:43:24.000Z","updated_at":"2025-02-07T11:58:44.000Z","dependencies_parsed_at":"2023-05-26T04:15:33.556Z","dependency_job_id":null,"html_url":"https://github.com/rafael-santiago/aegis","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafael-santiago%2Faegis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafael-santiago%2Faegis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafael-santiago%2Faegis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafael-santiago%2Faegis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rafael-santiago","download_url":"https://codeload.github.com/rafael-santiago/aegis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299766,"owners_count":20916183,"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":["anti-debug","cgo","cgo-bindings","cross-platform","debugging-facility","golang","golang-package","information-security","infosec","linux-hacking-tools","linux-library","minimalistic","procfs","ptrace","software-protection","suckless","system-hacking","system-programming","unix","unix-extension"],"created_at":"2024-11-05T13:05:39.330Z","updated_at":"2025-04-05T06:30:47.858Z","avatar_url":"https://github.com/rafael-santiago.png","language":"Go","funding_links":[],"categories":["Detection Engines"],"sub_categories":[],"readme":"![Medusa by Caravaggio (1571-1610) / Public Domain](https://github.com/rafael-santiago/aegis/blob/master/etc/aegis_logo.png \"Medusa by Caravaggio (1571-1610) / Public Domain\")\n#\n\n``Aegis`` is a library that allows you detect if your software is being debugged or not on ``Linux``, ``FreeBSD``, ``NetBSD``, ``OpenBSD`` and\n``Windows``. You can use it natively from ``C`` or use the ``Go`` bind.\n\nThe name is about a lousy acronym: **A**n **E**LF's -**g** **i**nspection **s**ignalling. If you are hooked on\nGreek mythology you should know that ``Aegis`` is the name of the shield gave by ``Athena`` to ``Perseus`` to help him\nkill ``Medusa``. If you are using it from ``Windows`` understand as **A**n **E**xecutable's -**g** **i**nspection\n**s**ignalling ;)\n\nOn ``Windows`` we have plenty of ways to easily do this kind of detection. Opposingly, on ``Unix`` world we do not have any\nstandard way. ``Aegis`` is an attempt of filling up this gap.\n\nYou can use ``Aegis`` as an ``anti-debugging mitigation`` or as a ``debugging facility``. It just depends on you and your\ncurrent requirements.\n\nOriginally, I wrote this library to use on another tool of mine called [``blackcat``](https://github.com/rafael-santiago/blackcat)\nas an ``anti-debugging`` stuff.\n\n# Contents\n\n- [How can I build it?](#how-can-i-build-it)\n    - [How should I easily clone ``Aegis``?](#how-should-i-easily-clone-aegis)\n    - [Build it by using ``Hefesto``](#building-it-by-using-hefesto)\n    - [Poor man's build by using make](#poor-mans-build-by-using-make)\n    - [Making a distribution package](#making-a-distribution-package)\n    - [How should I build ``Aegis`` ``Go`` stuff?](#how-should-i-build-aegis-go-stuff)\n- [Using Aegis](#using-aegis)\n    - [Debugging detection](#debugging-detection)\n        - [Testing ``wait4debug``](#testing-wait4debug)\n    - [Debugging mitigation](#debugging-mitigation)\n        - [Testing ``setgorgon``](#testing-setgorgon)\n    - [``Aegis`` from ``Go``](#aegis-from-go)\n        - [``wait4debug`` on ``Go``](#wait4debug-on-go)\n        - [What about a ``Gopher Gorgon``?](#what-about-a-gopher-gorgon)\n\n## How can I build it?\n\nI am using a build tool of mine called [``Hefesto``](https://github.com/rafael-santiago/hefesto) (Yes, mythology, I love it).\n\nIf you are looking for running the build in all its capabilities you need ``Hefesto`` otherwise I also supply a well-simple\nMakefile.\n\n[``Back``](#contents)\n\n### How should I easily clone ``Aegis``?\n\nThe easiest way is:\n\n```\nblack-beard@QueensAnneRevenge:~/src# git clone https://github.com/rafael-santiago/aegis --recursive\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\n[``Back``](#contents)\n\n### Building it by using ``Hefesto``\n\nAfter following all steps to put ``Hefesto`` to work on your system, just change to ``src`` sub-directory:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis# cd src\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nThe hardest part: invoke ``Hefesto``. Look:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# hefesto\n(...)\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nIf all has occurred fine during your build, ``aegis`` library was built at ``../lib`` sub-directory. Additionaly,\ntest has ran and all samples was built at ``../samples`` sub-directory.\n\nIn order to skip tests you must invoke ``Hefesto`` with the option ``--no-tests``:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# hefesto --no-tests\n(...)\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\n[``Back``](#contents)\n\n### Poor man's build by using ``make``\n\nWell this will just build the library at ``../lib``. The clumsy idea here is: If all has compiled so all is working...\n\nChange to ``src`` sub-directory:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis# cd src\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nNow it is just about calling ``make``:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# make\n(...)\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nIf you are on some ``BSD-like``, besides ``make`` you also need ``gmake`` to run this limited alternative build.\n\nOn ``*BSD`` you can also invoke the ``poor man's build`` by running ``gmake``.\n\n[``Back``](#contents)\n\n### Making a distribution package\n\nYou can easily do it by invoking ``Hefesto`` passing the build option ``--mkdist``:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# hefesto --mkdist\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nOnce done the distribution package will be at ``../lib`` sub-directory. The package's name depends on your system.\nIt will follow this nomenclature scheme: ``libaegis-\u003cos-name\u003e.zip``.\n\n[``Back``](#contents)\n\n### How should I build ``Aegis`` ``Go`` stuff?\n\n``Go`` is a language with automagically build capabilities. Once inside package sub-directory (``gopkg/vN``) call\n``go build -a`` or ``go test`` will do the job. For samples, once inside a sample directory run ``go build -a -o sample-name``.\nIt would be the poor man's build for ``Go``.\n\nIf you are in a rush and looking for a more automated way of doing it. Inside ``src`` top-level sub-directory invoke ``Hefesto``\npassing ``--gopkg`` build option:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# hefesto --gopkg\n(...)\nblack-beard@QueensAnneRevenge:~/src/aegis/src# _\n```\n\nAfter a successful build you will got the ``Go`` samples inside ``../samples`` with their names prepended with ``golang-``.\n\n[``Back``](#contents)\n\n## Using ``Aegis``\n\n``Aegis`` is a well-simple tiny library:\n\n- It only has one header called ``aegis.h``.\n\n- It only has one library archive called ``libaegis.a``.\n\n``Aegis`` brings you two features:\n\n- Detect debugging.\n\n- Protect against debugging by using our simpathetic ``Gorgon`` (yes, I know, Greek mythology again).\n\n**Remark**: In order to make this anti-debug resilient against library hooking you always should link your software ``static``.\nMoreover, if you want to keep eavesdroppers out as much as possible: link your software static. Otherwise there is no necessity\nof worrying about none of it. Because your front door is wide open or you do not have even a door! Haha!\n\n[``Back``](#contents)\n\n### Debugging detection\n\nIn some bug hunting cases is useful to wait for debugger before continuing the program execution. Specially concurrent stuff or\neven event oriented processing. In this case you can use ``aegis_has_debugger()`` function.\n\nThis function returns 1 when a debugger has being attached otherwise 0.\n\nThe following program will wait for debugger before exiting:\n\n```c\n#include \u003caegis.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003cunistd.h\u003e\n#include \u003csignal.h\u003e\n\nvoid sigint_watchdog(int signo) {\n    printf(\"\\nCanceled.\\n\");\n    exit(1);\n}\n\nint main(int argc, char **argv) {\n    signal(SIGINT, sigint_watchdog);\n    signal(SIGTERM, sigint_watchdog);\n    printf(\"*** Waiting for debug attachment (pid=%d)...\\n\", getpid());\n    while (!aegis_has_debugger()) {\n        usleep(1);\n    }\n    printf(\"*** Debugger is attached.\\n\");\n    return 0;\n}\n```\n\nThe program above can be found at ``src/samples`` sub-directory under the name ``wait4debug.c``.\nThe manual compilation of this code is fairly simple and involves:\n\n- To indicate where ``aegis.h`` is found.\n- To indicate where ``libaegis.a`` is found.\n- To pass ``-laegis`` flag to linker.\n- To pass ``-lpthread`` flag if you are on ``Linux``, ``FreeBSD``, ``NetBSD`` or ``OpenBSD``.\n\nAll in one compilation line:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src# cd samples\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# gcc -I.. -L../../lib \\\n\u003e wait4debug.c -owait4debug -laegis\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# _\n```\n\n[``Back``](#contents)\n\n#### Testing ``wait4debug``\n\nOn a terminal run ``wait4debug``:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./wait4debug\n*** Wait for debug attachment (pid=29670)\n```\n\n``wait4debug`` has facilitate the things for you by giving its ``pid``. Now on another terminal run ``GDB`` as follows:\n\n```\nblack-beard@QueensAnneRevenge:~# gdb attach 29670\n(...)\n(gdb)\n```\n\nYou will attach to the ``wait4debug`` process, now let's continue on ``GDB``:\n\n```\nblack-beard@QueensAnneRevenge:~# gdb attach 29670\n(...)\n(gdb) continue\nContinuing.\n[Inferior 1 (process 29670) exited normally]\n(gdb)\n```\n\nNice, the program has exited. If you back to your ``wait4debug`` terminal you will see something like:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./wait4debug\n*** Wait for debug attachment (pid=29670)\n*** Debugger is attached.\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# _\n```\n\n[``Back``](#contents)\n\n### Debugging mitigation\n\nCertain programs require some debugging avoidance. ``Aegis`` features a nice and straightforward way to implement this kind\nof mitigation. For doing that you need:\n\n- To implement a exit checking function with the prototype: ``int(void *)``. A return different from zero means that gorgon should exit.\n- If necessary to implement a on debugger function with the prototype: ``void(void *)``. This function will be called when a debugger is detected.\n- To call ``aegis_set_gorgon()`` passing your exit checking function and its argument pointer, besides on debugger function and its argument pointer.\n\nTake a look at the following code to get more details about:\n\n```c\n/*\n * Copyright (c) 2020, Rafael Santiago\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree.\n */\n#include \u003caegis.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cunistd.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003csignal.h\u003e\n\nint bye = 0;\n\nvoid sigint_watchdog(int signo) {\n    bye = 1;\n}\n\nint disable_gorgon(void *args) {\n    return (*(int *)args);\n}\n\nvoid on_debugger(void *args) {\n    fprintf(stdout, \"\\ninfo: debugger detected.\\n\");\n    exit(1);\n}\n\nint main(int argc, char **argv) {\n    signal(SIGINT, sigint_watchdog);\n    signal(SIGTERM, sigint_watchdog);\n    if (aegis_set_gorgon(disable_gorgon, \u0026bye, on_debugger, NULL) != 0) {\n        fprintf(stderr, \"error: unable to set gorgon.\\n\");\n        exit(1);\n    }\n\n    fprintf(stdout, \"info: process started (pid=%d)...\\n\", getpid());\n    while (!bye) {\n        usleep(2);\n    }\n\n    fprintf(stdout, \"\\ninfo: gracefully exiting, no debugger was detected.\\n\");\n\n    return 0;\n}\n```\n\nYou can find the presented code into ``src/samples/setgorgon.c``.\n\nWhen on debugger function was passed as ``NULL``. It asks ``aegis`` to use its default on debugger function. This\ndefault callback is only about calling ``exit(1)``. Thus, after a debugger detection, the process will immediately exit.\nThere are some cases that you need to do something before exiting, for those cases on debugger function would be handy.\nAnyway, once a debugger attached, the best action is terminate the process as soon as possible or try to kill the debugger.\n\n[``Back``](#contents)\n\n#### Testing ``setgorgon``\n\nOn a terminal run ``setgorgon``\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./setgorgon\ninfo: process started (pid=28582)...\n```\n\nNow let's only press ``ctrl + c``:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./setgorgon\ninfo: process started (pid=28582)...\n^C\ninfo: gracefully exiting, no debugger was detected.\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# _\n```\n\nNice but what about give debugging a try? Let's run it again:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./setgorgon\ninfo: process started (pid=14753)...\n```\n\nOn another terminal let's attach ``GDB``:\n\n```\nblack-beard@QueensAnneRevenge:~# gdb attach 14753\n(...)\n(gdb) _\n```\n\nNow, still on ``GDB`` continue the ``setgorgon`` process:\n\n```\nblack-beard@QueensAnneRevenge:~# gdb attach 14753\n(...)\n(gdb) continue\nContinuing.\n[Thread 0x7f47880ec700 (LWP 14754) exited]\n[Inferior 1 (process 14753) exited with code 01]\n(gdb) _\n```\n\nNo chance for our debugging attempt, let's go back to our setgorgon's terminal:\n\n```\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# ./setgorgon\ninfo: process started (pid=14753)...\n\ninfo: debugger detected.\nblack-beard@QueensAnneRevenge:~/src/aegis/src/samples# _\n```\n\nNo gracefully exiting message, it was really aborted to avoid debugging. Our gorgon has done her job.\n\nMaybe you are asking why call this feature of ``Gorgon``. Well, ``Perseus`` myth tells that ``Athena`` gave him a shield (aegis)\nfor help him to kill ``Medusa`` (also known as ``Gorgon``).``Perseus`` has killed her using ``aegis``. By watching for her\nreflection in the shield he used the sword (also given by ``Athena``) to chop off ``Medusa``'s head. After that ``Athena`` has\npicked ``aegis`` back and in memory of ``Medusa``, ``Athena`` put ``Medusa``'s head in this shield.\n\nAncient greeks had used to sculpt or even drawn ``Gorgon`` heads at the front door of their houses in order to scare enemies,\nbad people, bad things and stuff. Maybe it could be the origin of the ``Medusa``'s myth, who knows...\n\nWell, that is it, here we are using gorgon to scare debuggers! That's all folks!\n\n;)\n\n[``Back``](#contents)\n\n### ``Aegis`` from ``Go``\n\nI have decided to make an ``Aegis``' ``Go`` bind because I am watching many applications related to information security\nbeing written mainly on ``Go``, showing up during these years (2020). Who knows this bind can be useful for somebody\nsomewhere over a concurrent multiplatform goroutine rainbow... ``Go`` is also my second best programming language so\nI have done it for fun, too. It was a good excuse for using ``Cgo``.\n\nBasically, you need to import aegis package from this repo:\n\n```go\nimport (\n    \"github.com/rafael-santiago/aegis/gopkg\"\n)\n```\n\nAfter you will define in your ``go.mod`` the following:\n\n```\n(...)\nreplace github.com/rafael-santiago/aegis/gopkg =\u003e github.com/rafael-santiago/aegis/gopkg/v2\n(...)\n```\n\nYou can also host it as a local package (no problem). Take a look how it can be done by taking a look at ``go.mod`` from\n``Go`` samples (``gopkg/samples``).\n\nThis replace trick will allow you use ``Aegis``' future releases without needing be noisy into your own related code.\nRenaming packages and all those ``MacGyver-like`` incantations, ready to go...\n\nThe usage of ``Aegis`` on ``Go`` is almost the same of its usage in ``C``. Follow on reading if you are interested on it.\n\n[``Back``](#contents)\n\n#### ``wait4debug`` on ``Go``\n\nI am taking into consideration that you have already followed my notes about ``wait4debug`` C sample. Doing it\non ``Go`` is quite straightforward, too. It is only about testing the attachment state by calling ``aegis.HasDebugger()``\noracle function, look:\n\n```go\n//\n// Copyright (c) 2020, Rafael Santiago\n// All rights reserved.\n//\n// This source code is licensed under the BSD-style license found in the\n// LICENSE file in the root directory of this source tree.\n//\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/rafael-santiago/aegis/gopkg\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n)\n\nfunc main() {\n\tgo func() {\n\t\tsigintWatchdog := make(chan os.Signal, 1)\n\t\tsignal.Notify(sigintWatchdog, os.Interrupt)\n\t\tsignal.Notify(sigintWatchdog, syscall.SIGINT|syscall.SIGTERM)\n\t\t\u003c-sigintWatchdog\n\t\tfmt.Fprintf(os.Stdout, \"\\rinfo: ctrl + C received. Aborted.\\n\")\n\t\tos.Exit(1)\n\t}()\n\tfmt.Fprintf(os.Stdout, \"info: Waiting for debug attachment (pid=%d)...\\n\",\n\t\tos.Getpid())\n\tfor !aegis.HasDebugger() {\n\t\ttime.Sleep(1 * time.Nanosecond)\n\t}\n\tfmt.Fprintf(os.Stdout, \"\\rinfo: Debug detected. Go home!\\n\")\n}\n```\n\nThe program will wait for a user's ``Ctrl + c`` interruption or for a debugger attaching. It is always important to\nsleep for some time interval, otherwise you will busy the main thread and cause starvation on other threads.\n\n[``Back``](#contents)\n\n#### What about a ``Gopher Gorgon``?\n\nWell, if you have some artistic inclination and want to make a ``Medusa-like gopher`` to put here I would be thankful haha!\n\nAnyway, use ``Aegis Gorgon`` in ``Go`` is pretty straightforward, too. You just call ``aegis.SetGorgon()``. Take a look:\n\n```go\n//\n// Copyright (c) 2020, Rafael Santiago\n// All rights reserved.\n//\n// This source code is licensed under the BSD-style license found in the\n// LICENSE file in the root directory of this source tree.\n//\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/rafael-santiago/aegis/gopkg\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\"time\"\n)\n\n// INFO(Rafael): This function flags for aegis.SetGorgon() if it is time to stop watching and exit.\nfunc shouldExit(exit interface{}) bool {\n\texitChan := exit.(chan bool)\n\tvar should bool = false\n\ttimeout := time.Tick(1 * time.Nanosecond)\n\tselect {\n\tcase should = \u003c-(exitChan):\n\tcase \u003c-timeout:\n\t}\n\treturn should\n}\n\nfunc main() {\n\texit := make(chan bool, 1)\n\tgo func(exit chan\u003c- bool) {\n\t\tsigintWatchdog := make(chan os.Signal, 1)\n\t\tsignal.Notify(sigintWatchdog, os.Interrupt)\n\t\tsignal.Notify(sigintWatchdog, syscall.SIGINT|syscall.SIGTERM)\n\t\t\u003c-sigintWatchdog\n\t\texit \u003c- true\n\t\tfmt.Fprintf(os.Stdout,\n\t\t\t\"\\rinfo: ctrl + c received from the user. Exiting...\\n\")\n\t}(exit)\n\tfmt.Fprintf(os.Stdout, \"info: process started (pid=%d)...\\n\", os.Getpid())\n\t// INFO(Rafael): Let's be less ambitious here. We will just pass our\n\t//               gracefully exit check function and its argument.\n\t//               OnDebugger and OnDebuggerArgs will be null, with this\n\t//               we will use the default Aegis' on debugger function\n\t//               that is only about a gross os.Exit(1) \u003e:P\n\tgo aegis.SetGorgon(shouldExit, exit, nil, nil)\n\t// INFO(Rafael): All your sensitive instructions not suitable for\n\t// eavesdroppers would go here. On some requirements you will need\n\t// to flush some buffers, files and stuff before exiting. For those\n\t// cases you would pass your custom OnDebugger and OnDebuggerArgs\n\t// to Aegis' Gorgon.\n\t\u003c-exit\n}\n```\n\nThe program will run until detecting a debugger be attached or being asked for gracefully exiting through a ``ctrl + C``.\n\n[``Back``](#contents)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafael-santiago%2Faegis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frafael-santiago%2Faegis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafael-santiago%2Faegis/lists"}