Skip to content

Commit

Permalink
?classSysGetOwnPath(): _Termux_ linker64 fix,
Browse files Browse the repository at this point in the history
	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: termux/termux-exec#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: d50262f (+`classSysGetOwnPath()`, +`classSysFopenOwnPath()`), 5f0ffd8 (?`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.
  • Loading branch information
SwuduSusuwu committed Nov 23, 2024
1 parent d50262f commit f331489
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 30 deletions.
16 changes: 12 additions & 4 deletions cxx/ClassSys.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
#include "ClassPortableExecutable.hxx" /* FilePath */
#include <cassert> /* assert */
#include <cerrno> /* errno */
#include IF_SUSUWU_CPLUSPLUS(<cstdio>, <stdio.h>) /* FILE fopen */
#include IF_SUSUWU_CPLUSPLUS(<cstdio>, <stdio.h>) /* FILE fopen PATH_MAX */
#include <cstdlib> /* exit EXIT_FAILURE EXIT_SUCCESS getenv strtol */
#include <iostream> /* std::cerr std::cout std::endl std::flush std::ios::eofbit std::ios::goodbit */
#ifdef SUSUWU_POSIX
#include <stdexcept> /* std::runtime_error */
#include <sys/types.h> /* pid_t */
#include <sys/wait.h> /* waitpid WIFEXITED WEXITSTATUS WIFSIGNALED WSIGTERM */
#include <unistd.h> /* execve execv fork geteuid getuid setuid */
#include <unistd.h> /* execve execv fork geteuid getuid readlink setuid */
#else
# ifdef SUSUWU_WIN32
# include <windows.h> /* GetModuleFileName GetModuleHandle HMODULE */
Expand Down Expand Up @@ -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]);
Expand Down
6 changes: 4 additions & 2 deletions cxx/ClassSys.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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<FilePath>::value) */;
const FILE *classSysFopenOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_invocable<classSysGetFilePath()>::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 `<ctrl>+d`, thus input prompts will use default choices.) Returns `classSysGetConsoleInput();` */
Expand Down
10 changes: 5 additions & 5 deletions cxx/VirusAnalysis.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ std::vector<VirusAnalysisFun> 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",
""
};
}
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",
Expand All @@ -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). */"));
Expand Down Expand Up @@ -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 */
}
}
}
Expand Down Expand Up @@ -264,7 +264,7 @@ const std::vector<std::string> 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.
*
Expand Down
40 changes: 21 additions & 19 deletions posts/VirusAnalysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<FilePath>::value) */;
const FILE *classSysFopenOwnPath() /* TODO: SUSUWU_NOEXCEPT(std::is_nothrow_invocable<classSysGetFilePath()>::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 `<ctrl>+d`, thus input prompts will use default choices.) Returns `classSysGetConsoleInput();` */
Expand Down Expand Up @@ -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)
```
Expand Down Expand Up @@ -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 */
}
Expand Down Expand Up @@ -1267,15 +1276,15 @@ std::vector<VirusAnalysisFun> 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",
""
};
}
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",
Expand All @@ -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). */"));
Expand Down Expand Up @@ -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 */
}
}
}
Expand Down Expand Up @@ -1487,7 +1496,7 @@ const std::vector<std::string> 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.
*
Expand Down Expand Up @@ -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`
*/

1 comment on commit f331489

@SwuduSusuwu
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StackOverflow says stdio.h has PATH_MAX; auto build failed since GitHub's runner does not have PATH_MAX from such #include.
Since commit which fail to build cannot use git bisect, it was replaced with af5226c (which has #include "limits.h" /* PATH_MAX */)

Please sign in to comment.