From f3314894dbc7f768e406f270e14a02ae9153534b Mon Sep 17 00:00:00 2001 From: SwuduSusuwu <2002luvabbaluvu@gmail.com> Date: Fri, 22 Nov 2024 20:42:56 -0800 Subject: [PATCH] ?`classSysGetOwnPath()`: _Termux_ `linker64` fix, so that `virusAnalysisTests()` outputs the offset+hash of `argv[0]`. Note that just the _Google Store_ version of _Termux_ output the wrong offset+hash (due to _Google_'s rules: https://github.com/termux/termux-exec/pull/24). In the future (if more is done with our path than output example offsets+hashes), it is possible that this fix will have more value. If other programs use `classSysGetOwnPath()` on _Termux_, this can improve those. ?`cxx/ClassSys.cxx`: ?`classSysGetOwnPath()`: the fix was to use `readlink` to return true path. ?`cxx/ClassSys.hxx`: ?`classSysGetOwnPath()`, ?`classSysFopenOwnPath()`: comments improved. ?`cxx/VirusAnalysis.cxx`: typo fixes. Closes issue #26 (Improve: cxx/ClassSys.cxx:classSysGetOwnPath(): for _Termux_, use `procfs` to return our true path.) Is followup to: d50262f78154622a6003adcada36402254995b1c (+`classSysGetOwnPath()`, +`classSysFopenOwnPath()`), 5f0ffd8de5c7f0236a71bdc905cc4b0439ddf33e (?`virusAnalysisTests()`: if Linux, use procfs: this should close https://github.com/SwuduSusuwu/SubStack/security/code-scanning/1277). ?`posts/VirusAnalysis.md`: Include all this. Remove accidental duplicate comments at end of file. --- cxx/ClassSys.cxx | 16 ++++++++++++---- cxx/ClassSys.hxx | 6 ++++-- cxx/VirusAnalysis.cxx | 10 +++++----- posts/VirusAnalysis.md | 40 +++++++++++++++++++++------------------- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/cxx/ClassSys.cxx b/cxx/ClassSys.cxx index c7e3eef5..d6852932 100644 --- a/cxx/ClassSys.cxx +++ b/cxx/ClassSys.cxx @@ -6,14 +6,14 @@ #include "ClassPortableExecutable.hxx" /* FilePath */ #include /* assert */ #include /* errno */ -#include IF_SUSUWU_CPLUSPLUS(, ) /* FILE fopen */ +#include IF_SUSUWU_CPLUSPLUS(, ) /* FILE fopen PATH_MAX */ #include /* exit EXIT_FAILURE EXIT_SUCCESS getenv strtol */ #include /* std::cerr std::cout std::endl std::flush std::ios::eofbit std::ios::goodbit */ #ifdef SUSUWU_POSIX #include /* std::runtime_error */ #include /* pid_t */ #include /* waitpid WIFEXITED WEXITSTATUS WIFSIGNALED WSIGTERM */ -#include /* execve execv fork geteuid getuid setuid */ +#include /* execve execv fork geteuid getuid readlink setuid */ #else # ifdef SUSUWU_WIN32 # include /* GetModuleFileName GetModuleHandle HMODULE */ @@ -155,17 +155,25 @@ const FILE *classSysFopenOwnPath() { } const FilePath classSysGetOwnPath() { #ifdef __linux__ - return "/proc/self/exe"; + char path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (len == -1) { + SUSUWU_ERROR("classSysGetOwnPath(): { if(-1 == readlink(\"/proc/self/exe\", path, sizeof(path) - 1)) {/* this shouldn't happen */} }"); + return FilePath(); /* return EXIT_FAILURE; */ + } + path[len] = '\0'; +// `return "/proc/self/exe"; /* if _Termux_, causes `PortableExecutableBytecode(classSysGetOwnPath())` to act as `PortableExecutableBytecode("/apex/com.android.runtime/bin/linker64")` */ + return FilePath(path); /* causes `PortableExecutableBytecode(classSysGetOwnPath())` to act as `PortableExecutableBytecode(argv[0])` */ #elif defined SUSUWU_WIN32 char ownPathStr[MAX_PATH]; HMODULE hModule = GetModuleHandle(SUSUWU_NULLPTR); if(hModule) { GetModuleFileName(hModule, ownPathStr, sizeof(ownPathStr)); + return FilePath(ownPathStr); } else { SUSUWU_ERROR("classSysGetOwnPath(): { if(!GetModuleHandle(NULL)) {/* this shouldn't happen */} }"); return FilePath(); /* return EXIT_FAILURE; */ } - return FilePath(ownPathStr); #else /* def SUSUWU_WIN32 else */ assert(SUSUWU_NULLPTR != classSysArgs); assert(SUSUWU_NULLPTR != classSysArgs[0]); diff --git a/cxx/ClassSys.hxx b/cxx/ClassSys.hxx index b428cbb3..05c95074 100644 --- a/cxx/ClassSys.hxx +++ b/cxx/ClassSys.hxx @@ -52,8 +52,10 @@ const bool classSysHasRoot(); const bool classSysSetRoot(bool root); /* root ? (seteuid(0) : (seteuid(getuid() || atoi(getenv("SUDO_UID"))), setuid(geteuid)); return classSysHasRoot(); */ /* Filesystems */ -const FilePath classSysGetOwnPath(); -const FILE *classSysFopenOwnPath(); +/* Usage: for Linux (or Windows,) if you don't trust `argv[0]`, replace it with `classSysGetOwnPath()`. + * Error values: `return FilePath();` */ +const FilePath classSysGetOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_constructible::value) */; +const FILE *classSysFopenOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_invocable::value) */; static const bool classSysGetConsoleInput() { return std::cin.good() && !std::cin.eof(); } const bool classSysSetConsoleInput(bool input); /* Set to `false` for unit tests/background tasks (acts as if user pressed `+d`, thus input prompts will use default choices.) Returns `classSysGetConsoleInput();` */ diff --git a/cxx/VirusAnalysis.cxx b/cxx/VirusAnalysis.cxx index 7e4c9038..b3872905 100644 --- a/cxx/VirusAnalysis.cxx +++ b/cxx/VirusAnalysis.cxx @@ -42,7 +42,7 @@ std::vector virusAnalyses = {hashAnalysis, signatureAnalysis, const bool virusAnalysisTests() { ResultList abortOrNull; { - abortOrNull.hashes = {}, abortOrNull.signatures = {}, abortOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) infection databases */ + abortOrNull.hashes = {}, abortOrNull.signatures = {}, abortOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) infection databases */ "infection", "infectedSW", "corruptedSW", @@ -50,7 +50,7 @@ const bool virusAnalysisTests() { }; } ResultList passOrNull; { - passOrNull.hashes = {}, passOrNull.signatures = {}, passOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) fresh-files databases */ + passOrNull.hashes = {}, passOrNull.signatures = {}, passOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) fresh-files databases */ "", "SW", "SW", @@ -72,7 +72,7 @@ const bool virusAnalysisTests() { produceAnalysisCns(passOrNull, abortOrNull, ResultList(), analysisCns); produceVirusFixCns(passOrNull, abortOrNull, virusFixCns); const FilePath gotOwnPath = classSysGetOwnPath(); - if("" != gotOwnPath) { + if(FilePath() != gotOwnPath) { const PortableExecutableBytecode executable(gotOwnPath); /* https://github.com/SwuduSusuwu/SubStack/security/code-scanning/1277 ("Uncontrolled data used in path expression ") fix. */ if(virusAnalysisAbort == virusAnalysis(executable)) { throw std::runtime_error(SUSUWU_ERRSTR(ERROR, "{virusAnalysisAbort == virusAnalysis(args[0]);} /* With such false positives, shouldn't hook kernel modules (next test is to hook+unhook `exec*` to scan programs on launch). */")); @@ -225,7 +225,7 @@ const VirusAnalysisResult hashAnalysis(const PortableExecutable &file, const Res SUSUWU_NOTICE("hashAnalysis(/*.file =*/ \"" + file.path + "\", /*.fileHash =*/ 0x" + classSysHexStr(fileHash) + ") {return virusAnalysisAbort;} /* due to hash 0x" + classSysHexStr(fileHash) + " (found in `abortList.hashes`). You should treat this as a virus detection if this was not a test. */"); return hashAnalysisCaches[fileHash] = virusAnalysisAbort; } else { - return hashAnalysisCaches[fileHash] = virusAnalysisContinue; /* continue to next tests */ + return hashAnalysisCaches[fileHash] = virusAnalysisContinue; /* continue to next tests */ } } } @@ -264,7 +264,7 @@ const std::vector importedFunctionsList(const PortableExecutable &f * * "x86" instruction list for Intel/AMD ( https://wikipedia.org/wiki/x86 ), * "aarch64" instruction list for most smartphones/tablets ( https://wikipedia.org/wiki/aarch64 ), - * shows how to analyse what OS functions the SW goes to without libraries (through `int`/`syscall`, old; most new SW uses `jmp`/`call`.) + * shows how to analyse what OS functions the SW goes to without libraries (through `int`/`syscall`, old; most new SW uses `jmp`/`call`.) * Plus, instructions lists show how to analyse what args the apps/SW pass to functions/syscalls (simple for constant args such as "push 0x2; call functions;", * but if registers/addresses as args such as "push eax; push [address]; call [address2];" must guess what is *"eax"/"[address]"/"[address2]", or use sandboxes. * diff --git a/posts/VirusAnalysis.md b/posts/VirusAnalysis.md index fd4bac39..3f32d0ab 100644 --- a/posts/VirusAnalysis.md +++ b/posts/VirusAnalysis.md @@ -409,8 +409,10 @@ const bool classSysHasRoot(); const bool classSysSetRoot(bool root); /* root ? (seteuid(0) : (seteuid(getuid() || atoi(getenv("SUDO_UID"))), setuid(geteuid)); return classSysHasRoot(); */ /* Filesystems */ -const FilePath classSysGetOwnPath(); -const FILE *classSysFopenOwnPath(); +/* Usage: for Linux (or Windows,) if you don't trust `argv[0]`, replace it with `classSysGetOwnPath()`. + * Error values: `return FilePath();` */ +const FilePath classSysGetOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_constructible::value) */; +const FILE *classSysFopenOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_invocable::value) */; static const bool classSysGetConsoleInput() { return std::cin.good() && !std::cin.eof(); } const bool classSysSetConsoleInput(bool input); /* Set to `false` for unit tests/background tasks (acts as if user pressed `+d`, thus input prompts will use default choices.) Returns `classSysGetConsoleInput();` */ @@ -482,7 +484,6 @@ auto templateCatchAll(Func func, const std::string &funcName, Args... args) -> c /* @throw std::runtime_error */ const bool classSysTests(); static const bool classSysTestsNoexcept() SUSUWU_NOEXCEPT {return templateCatchAll(classSysTests, "classSysTests()");} - ``` `less` [cxx/ClassSys.cxx](https://github.com/SwuduSusuwu/SubStack/blob/trunk/cxx/ClassSys.cxx) ``` @@ -615,20 +616,28 @@ const FILE *classSysFopenOwnPath() { } const FilePath classSysGetOwnPath() { #ifdef __linux__ - return "/proc/self/exe"; + char path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (len == -1) { + SUSUWU_ERROR("classSysGetOwnPath(): { if(-1 == readlink(\"/proc/self/exe\", path, sizeof(path) - 1)) {/* this shouldn't happen */} }"); + return FilePath(); /* return EXIT_FAILURE; */ + } + path[len] = '\0'; +// `return "/proc/self/exe"; /* if _Termux_, causes `PortableExecutableBytecode(classSysGetOwnPath())` to act as `PortableExecutableBytecode("/apex/com.android.runtime/bin/linker64")` */ + return FilePath(path); /* causes `PortableExecutableBytecode(classSysGetOwnPath())` to act as `PortableExecutableBytecode(argv[0])` */ #elif defined SUSUWU_WIN32 char ownPathStr[MAX_PATH]; HMODULE hModule = GetModuleHandle(SUSUWU_NULLPTR); if(hModule) { GetModuleFileName(hModule, ownPathStr, sizeof(ownPathStr)); + return FilePath(ownPathStr); } else { SUSUWU_ERROR("classSysGetOwnPath(): { if(!GetModuleHandle(NULL)) {/* this shouldn't happen */} }"); return FilePath(); /* return EXIT_FAILURE; */ } - return FilePath(ownPathStr); #else /* def SUSUWU_WIN32 else */ - assert(NULL != classSysArgs); - assert(NULL != classSysArgs[0]); + assert(SUSUWU_NULLPTR != classSysArgs); + assert(SUSUWU_NULLPTR != classSysArgs[0]); return classSysArgs[0]; #endif /* def SUSUWU_WIN32 else */ } @@ -1267,7 +1276,7 @@ std::vector virusAnalyses = {hashAnalysis, signatureAnalysis, const bool virusAnalysisTests() { ResultList abortOrNull; { - abortOrNull.hashes = {}, abortOrNull.signatures = {}, abortOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) infection databases */ + abortOrNull.hashes = {}, abortOrNull.signatures = {}, abortOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) infection databases */ "infection", "infectedSW", "corruptedSW", @@ -1275,7 +1284,7 @@ const bool virusAnalysisTests() { }; } ResultList passOrNull; { - passOrNull.hashes = {}, passOrNull.signatures = {}, passOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) fresh-files databases */ + passOrNull.hashes = {}, passOrNull.signatures = {}, passOrNull.bytecodes = { /* Produce from an antivirus vendor's (such as VirusTotal.com's) fresh-files databases */ "", "SW", "SW", @@ -1297,7 +1306,7 @@ const bool virusAnalysisTests() { produceAnalysisCns(passOrNull, abortOrNull, ResultList(), analysisCns); produceVirusFixCns(passOrNull, abortOrNull, virusFixCns); const FilePath gotOwnPath = classSysGetOwnPath(); - if("" != gotOwnPath) { + if(FilePath() != gotOwnPath) { const PortableExecutableBytecode executable(gotOwnPath); /* https://github.com/SwuduSusuwu/SubStack/security/code-scanning/1277 ("Uncontrolled data used in path expression ") fix. */ if(virusAnalysisAbort == virusAnalysis(executable)) { throw std::runtime_error(SUSUWU_ERRSTR(ERROR, "{virusAnalysisAbort == virusAnalysis(args[0]);} /* With such false positives, shouldn't hook kernel modules (next test is to hook+unhook `exec*` to scan programs on launch). */")); @@ -1448,7 +1457,7 @@ const VirusAnalysisResult hashAnalysis(const PortableExecutable &file, const Res SUSUWU_NOTICE("hashAnalysis(/*.file =*/ \"" + file.path + "\", /*.fileHash =*/ 0x" + classSysHexStr(fileHash) + ") {return virusAnalysisAbort;} /* due to hash 0x" + classSysHexStr(fileHash) + " (found in `abortList.hashes`). You should treat this as a virus detection if this was not a test. */"); return hashAnalysisCaches[fileHash] = virusAnalysisAbort; } else { - return hashAnalysisCaches[fileHash] = virusAnalysisContinue; /* continue to next tests */ + return hashAnalysisCaches[fileHash] = virusAnalysisContinue; /* continue to next tests */ } } } @@ -1487,7 +1496,7 @@ const std::vector importedFunctionsList(const PortableExecutable &f * * "x86" instruction list for Intel/AMD ( https://wikipedia.org/wiki/x86 ), * "aarch64" instruction list for most smartphones/tablets ( https://wikipedia.org/wiki/aarch64 ), - * shows how to analyse what OS functions the SW goes to without libraries (through `int`/`syscall`, old; most new SW uses `jmp`/`call`.) + * shows how to analyse what OS functions the SW goes to without libraries (through `int`/`syscall`, old; most new SW uses `jmp`/`call`.) * Plus, instructions lists show how to analyse what args the apps/SW pass to functions/syscalls (simple for constant args such as "push 0x2; call functions;", * but if registers/addresses as args such as "push eax; push [address]; call [address2];" must guess what is *"eax"/"[address]"/"[address2]", or use sandboxes. * @@ -1990,10 +1999,3 @@ This post was about general methods to produce virus analysis tools, does not re Allows reuses of workflows which an existant analysis tool has -- can just add (small) local sandboxes, or just add artificial CNS to antivirus hosts for extra analysis. - * for MSVC: `git clone --depth 1 https://github.com/MicrosoftDocs/cpp-docs.git && vim cpp-docs/blob/main/docs/preprocessor/predefined-macros.md` or browse to https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros - * for others: `git clone https://github.com/cpredef/predef.git && vim predef/Compilers.md` - */ /* To pass new preprocessor definitions (example is `#define USE_CONTRACTS true`): - * to `clang`/`clang++`/`gcc`/`g++`/Intel(`icc`): `-DUSE_CONTRACTS=true` - * to MSVC(`cl`): `\DUSE_CONTRACTS=true` - */ -