Skip to content

Commit 6e233ba

Browse files
committed
Detect mount inconsistency
If the `/system` directory is mounted by KernelSU as overlayfs, then many detectors can still detect the mounting trace even the target is correctly unmounted. In Holmes V1.5.1, it is shown as `Inconsistent Mount`. In Hunter 6.41, it is shown as ``` found OVERLAYFS_SUPER_MAGIC, but no overlayfs mount found ``` I am going to figure out this detection. However, no luck via scanning /proc/self/fd yet.
1 parent ac36f83 commit 6e233ba

4 files changed

Lines changed: 134 additions & 1 deletion

File tree

app/src/main/cpp/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ set(CMAKE_CXX_STANDARD 20)
2929
# used in the AndroidManifest.xml file.
3030
add_library(${CMAKE_PROJECT_NAME} SHARED
3131
# List C/C++ source files with relative paths to this CMakeLists.txt.
32-
atexit.cpp elf_util.cpp native-lib.cpp smap.cpp solist.cpp vmap.cpp)
32+
atexit.cpp elf_util.cpp fstatfs.cpp native-lib.cpp smap.cpp solist.cpp vmap.cpp)
3333

3434
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC include)
3535
# Specifies libraries CMake should link to your target library. You

app/src/main/cpp/fstatfs.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include "fstatfs.hpp"
2+
3+
#include "logging.h"
4+
#include <dirent.h>
5+
#include <string>
6+
#include <sys/vfs.h>
7+
#include <sys/stat.h>
8+
#include <unistd.h>
9+
10+
// The magic number for OverlayFS, defined in kernel headers (e.g.,
11+
// linux/magic.h) We define it here to avoid dependency on kernel headers in the
12+
// build environment.
13+
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
14+
15+
// For comparison, the magic number for EXT4
16+
#define EXT4_SUPER_MAGIC 0xEF53
17+
18+
void check_system_fds() {
19+
const std::string fd_dir_path = "/proc/self/fd";
20+
DIR *dir = opendir(fd_dir_path.c_str());
21+
22+
if (!dir) {
23+
LOGD("Error: Could not open %s: %s", fd_dir_path.c_str(), strerror(errno));
24+
return;
25+
}
26+
27+
LOGD("Starting scan of inherited file descriptors...");
28+
29+
struct dirent *entry;
30+
while ((entry = readdir(dir)) != nullptr) {
31+
// Each entry name is a file descriptor number
32+
const std::string fd_str = entry->d_name;
33+
34+
// Skip '.' and '..' directories
35+
if (fd_str == "." || fd_str == "..") {
36+
continue;
37+
}
38+
39+
int fd = -1;
40+
try {
41+
fd = std::stoi(fd_str);
42+
} catch (const std::invalid_argument &e) {
43+
LOGD("Warning: Could not parse FD: %s", fd_str.c_str());
44+
continue;
45+
}
46+
47+
// Construct the full path to the symbolic link
48+
char symlink_path[PATH_MAX];
49+
snprintf(symlink_path, sizeof(symlink_path), "%s/%s", fd_dir_path.c_str(),
50+
fd_str.c_str());
51+
52+
// Use readlink to find out what file this FD points to
53+
char real_path[PATH_MAX];
54+
ssize_t len = readlink(symlink_path, real_path, sizeof(real_path) - 1);
55+
56+
if (len == -1) {
57+
// This can happen for sockets, pipes, etc. It's normal.
58+
continue;
59+
}
60+
61+
// readlink does not null-terminate, so we must do it ourselves.
62+
real_path[len] = '\0';
63+
std::string real_path_str(real_path);
64+
65+
// This is the filter you requested: only check files from /system
66+
if (real_path_str.rfind("/system/", 0) == 0) {
67+
68+
LOGD("Checking FD %d -> %s", fd, real_path_str.c_str());
69+
70+
struct stat stat_from_fstat;
71+
if (fstat(fd, &stat_from_fstat) == -1) {
72+
LOGD(" -> fstat() failed. Skipping.");
73+
continue;
74+
}
75+
76+
struct stat stat_from_stat;
77+
if (stat(real_path, &stat_from_stat) == -1) {
78+
LOGD(" -> stat() failed. Skipping.");
79+
continue;
80+
}
81+
82+
LOGD(" -> fstat() dev:inode = %llu:%llu",
83+
(unsigned long long)stat_from_fstat.st_dev,
84+
(unsigned long long)stat_from_fstat.st_ino);
85+
LOGD(" -> stat() dev:inode = %llu:%llu",
86+
(unsigned long long)stat_from_stat.st_dev,
87+
(unsigned long long)stat_from_stat.st_ino);
88+
89+
struct statfs fs_info;
90+
// The CRITICAL part: call fstatfs on the integer FD, not statfs on the
91+
// path.
92+
if (fstatfs(fd, &fs_info) == -1) {
93+
LOGD(" -> fstatfs failed: %s", strerror(errno));
94+
continue;
95+
}
96+
97+
// Analyze the result
98+
if (fs_info.f_type == OVERLAYFS_SUPER_MAGIC) {
99+
LOGD(" -> Filesystem type: 0x%lX. *** OVERLAYFS DETECTED! ***",
100+
fs_info.f_type);
101+
} else if (fs_info.f_type == EXT4_SUPER_MAGIC) {
102+
LOGD(" -> Filesystem type: 0x%lX. (This is ext4)", fs_info.f_type);
103+
} else {
104+
LOGD(" -> Filesystem type: 0x%lX. (Unknown)", fs_info.f_type);
105+
}
106+
}
107+
}
108+
109+
closedir(dir);
110+
LOGD("File descriptor scan complete.");
111+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
3+
#include <cerrno> // For errno
4+
#include <cstring> // For strerror
5+
#include <dirent.h> // For opendir, readdir, closedir
6+
#include <sys/statfs.h> // For fstatfs
7+
#include <unistd.h> // For readlink
8+
9+
/**
10+
* @brief Iterates through all open file descriptors for the current process.
11+
*
12+
* For each file descriptor that is a symbolic link to a path starting with
13+
* "/system/", this function performs an fstatfs() call on the descriptor itself
14+
* (not the path). It logs the file descriptor number, its resolved path, and
15+
* the filesystem type, specifically highlighting if an overlayfs is detected.
16+
* This is designed to find traces of overlayfs mounts that have been hidden
17+
* from the current mount namespace but persist through inherited file
18+
* descriptors.
19+
*/
20+
void check_system_fds();

app/src/main/cpp/native-lib.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "atexit.hpp"
2+
#include "fstatfs.hpp"
23
#include "logging.h"
34
#include "smap.h"
45
#include "solist.hpp"
@@ -22,6 +23,7 @@ Java_org_matrix_demo_MainActivity_stringFromJNI(JNIEnv *env,
2223
if (g_array != nullptr) {
2324
LOGD("g_array status: %s", g_array->format_state_string().c_str());
2425
}
26+
check_system_fds();
2527

2628
if (abnormal_soinfo != nullptr) {
2729
solist_detection =

0 commit comments

Comments
 (0)