termux / proot

An chroot-like implementation using ptrace.
https://wiki.termux.com/wiki/PRoot
Other
742 stars 162 forks source link

execve args replace #273

Closed w296488320 closed 1 year ago

w296488320 commented 1 year ago

Hi great developers, I had a problem recently about the execve parameter replacement. My requirement is to replace and modify (parameter 1) and (parameter 2) before some execve commands execute. But I refer to the proroot code now and it doesn t seem to take effect. If the parameters are not modified, the program will print the content normally, but if the parameter is modified, the program will not print any information. I tried to reconstruct the logic of enter.c in execve, which is the code I modified:

//int execve(const char *pathname, char *const argv[], char *const envp[]);
        case SC_execve: {
            status = 0;
            if (getRuntimeIsFinsh()) {
                char org_path_buff[PATH_MAX];
                get_sysarg_path(tracee, org_path_buff, SYSARG_1);

                ArrayOfXPointers *args_array;
                fetch_array_of_xpointers(tracee, &args_array, SYSARG_2, 0);
                string orig_args, orig_cmd_path(org_path_buff);
                size_t args_count = args_array->length;
                for (size_t i = 0; i < args_count - 1; i++) {
                    char *arg_str;
                    read_xpointee_as_string_t(tracee, args_array, i, &arg_str);
                    if (i != 0) {
                        orig_args.append(" ");
                    }
                    orig_args.append(arg_str);

                }
                //get mock value 
                auto handler_info = ZhenxiRunTime::handlerExecve::handler(orig_args);

                if (handler_info.isHandler) {
                    //set cmd path 
                    set_sysarg_path(tracee, handler_info.cmd_path.c_str(), SYSARG_1);

                    vector<string> new_args_list = handler_info.args;
                    resize_array_of_xpointers(args_array, 0, (ssize_t) (new_args_list.size() + 1));

                    for (size_t i = 0; i < new_args_list.size(); i++) {
                        write_xpointee(args_array, i, new_args_list[i].c_str());
                    }
                    //nullptr
                    write_xpointee(args_array, new_args_list.size(), nullptr);
                    //set args 
                    status = push_array_of_xpointers_t(tracee, args_array, SYSARG_2);
                    if (status < 0) {
                        LOGE("svc execve error  push_array_of_xpointers %d", status);
                        break;
                    }
                }
            }
            break;
        }

This is the code that I tested, and the main purpose is to replace the return value of [stat-f /], with [cat my_file_path]

    const char *logcatPath = "/system/bin/stat";
    const char *logcatArgs[] = { "stat", "-f", "/", nullptr,"111","222" };
    int pipefd[2];
    if (pipe(pipefd) == -1) {
        LOGE("pipe error")
        return;
    }
    pid_t pid = fork();
    if (pid < 0) {
        LOGE("fork");
        return;
    } else if (pid == 0) {

        close(pipefd[0]); 
        dup2(pipefd[1], STDOUT_FILENO);
        dup2(pipefd[1], STDERR_FILENO);
        close(pipefd[1]); 

        //int ret = (int)syscall(__NR_execve,logcatPath, (char *const *)logcatArgs, nullptr);
        int ret = execve(logcatPath, (char *const *)logcatArgs, nullptr);

        if (ret < 0) {
            LOGE("test execve error ret < 0 %s  ", strerror(errno))
            _exit(EXIT_FAILURE);
        }
        LOGE("test execve success ret %d",ret)
    } else {

        close(pipefd[1]);

        char buffer[1024];
        ssize_t bytesRead;
        //read
        while ((bytesRead = read(pipefd[0], buffer, sizeof(buffer) - 1)) > 0) {
            buffer[bytesRead] = '\0';
            LOGE("test execve printf ->  %s", buffer)
            //break;
        }
        close(pipefd[0]); 
        int status;
        waitpid(pid, &status, 0);
    }

The problem now is that as long as I make a parameter modification and replacement

LOGE("test execve printf ->  %s", buffer)

This log will not be printed, the program does not have any translation, if it is ok, I tried to change the stat command to [cat my_file_path] or [sh-c 'cat my_file_path'], I don't know how to solve this problem. Can you help me with something? Great developer

michalbednarski commented 1 year ago

I have no idea where you've put that code (and SC_execve looks unfamiliar), but on my side that variant worked

(Values were hardcoded and snippet was ported to C, tested within proot-distro ubuntu)

diff --git a/src/extension/fake_id0/fake_id0.c b/src/extension/fake_id0/fake_id0.c
index 9477f1c..30458b4 100644
--- a/src/extension/fake_id0/fake_id0.c
+++ b/src/extension/fake_id0/fake_id0.c
@@ -44,6 +44,7 @@
 #include "tracee/abi.h"
 #include "tracee/mem.h"
 #include "execve/auxv.h"
+#include "execve/aoxp.h"
 #include "path/binding.h"
 #include "path/f2fs-bug.h"
 #include "arch.h"
@@ -1209,6 +1210,44 @@ int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t data1
    }
 #endif

+   case SYSCALL_ENTER_START: {
+       Tracee *tracee = TRACEE(extension);
+       if (get_sysnum(tracee, CURRENT) == PR_execve) {
+                char org_path_buff[PATH_MAX];
+                get_sysarg_path(tracee, org_path_buff, SYSARG_1);
+
+                ArrayOfXPointers *args_array;
+                fetch_array_of_xpointers(tracee, &args_array, SYSARG_2, 0);
+                char orig_args[ARG_MAX] = {};
+                size_t args_count = args_array->length;
+                for (size_t i = 0; i < args_count - 1; i++) {
+                    char *arg_str;
+                    read_xpointee_as_string(args_array, i, &arg_str);
+                    if (i != 0) {
+                        strncat(orig_args, " ", sizeof(orig_args) - 1);
+                    }
+                    strncat(orig_args, arg_str, sizeof(orig_args) - 1);
+                    
+                }
+                //get mock value 
+                bool handler_info_isHandler = !strcmp(orig_args, "stat -f /");
+                
+                if (handler_info_isHandler) {
+                    //set cmd path 
+                    set_sysarg_path(tracee, "/bin/cat", SYSARG_1);
+
+                    resize_array_of_xpointers(args_array, 0, 3);
+
+                        write_xpointee(args_array, 0, "/bin/cat");
+                        write_xpointee(args_array, 1, "/etc/fstab");
+                    //nullptr
+                    write_xpointee(args_array, 2, NULL);
+                   //set args 
+                    push_array_of_xpointers(args_array, SYSARG_2);
+                }
+       }
+       break;
+   }
    case SYSCALL_ENTER_END: {
        Tracee *tracee = TRACEE(extension);
        Config *config = talloc_get_type_abort(extension->config, Config);
w296488320 commented 1 year ago

I don t know how I should clarify my problem to you so I wrote an apk using Android. A complete review of my problems. This apk is a simple version of root. The main purpose is to intercept the svc entrance of execve at enter.cpp. Can I invite you to the project? The name of this project is ExecveSvcIntercept and below is my intercept code if comments open. Then the execve executed will not have any log printing.

int translate_syscall_enter(Tracer *tracee) {
    int flags;
    int dirfd;
    int olddirfd;
    int newdirfd;

    int status;
    int status2;

    char path[PATH_MAX];
    char oldpath[PATH_MAX];
    char newpath[PATH_MAX];

    Sysnum syscall_number = get_sysnum(tracee, ORIGINAL);
    bool special = false;

#ifdef CALL_SYS_NUM
    if(getRuntimeIsFinsh()){
        const char *sysnum = stringify_sysnum(syscall_number);
        LOGE("invoke svc num -> %s ",sysnum)
    }
#endif

    switch (syscall_number) {
        //int execve(const char *pathname, char *const argv[], char *const envp[]);
        case SC_execve: {
            LOGE("start SC_execve enter ")
            status = 0;
            char org_path_buff[PATH_MAX];
            get_sysarg_path(tracee, org_path_buff, SYSARG_1);

            ArrayOfXPointers *args_array;
            fetch_array_of_xpointers(tracee, &args_array, SYSARG_2, 0);
            string orig_args, orig_cmd_path(org_path_buff);
            for (size_t i = 0; i < args_array->length; i++) {
                char *arg_str;
                read_xpointee_as_string_t(tracee, args_array, i, &arg_str);
                if (arg_str != nullptr) {
                    orig_args.append(arg_str).append(" ");
                }
            }
            LOGE(">>>>>>>>> execve  ->  [%s]  [%s] ", orig_cmd_path.c_str(), orig_args.c_str())
            //todo if close this code ,normal execution
//            const ExecveInfo &handler_info = handler(orig_args);
//            if (handler_info.isHandler) {
//                //set cmd path 1
//                set_sysarg_path(tracee, handler_info.cmd_path.c_str(), SYSARG_1);
//                LOGE("start handler svc execve cmd path [%s]", handler_info.cmd_path.c_str())
//                vector<string> new_args_list = handler_info.args;
//                resize_array_of_xpointers(args_array, 0, (ssize_t) (new_args_list.size() + 1));
//
//                for (size_t i = 0; i < args_array->length; i++) {
//                    if (i < new_args_list.size()) {
//                        write_xpointee(args_array, i, new_args_list[i].c_str());
//                        LOGI("rep arg info (%zu)[%s] ", i, new_args_list[i].c_str())
//                    } else {
//                        write_xpointee(args_array, i, nullptr);
//                    }
//                }
//                //set arg 2
//                push_array_of_xpointers_t(tracee, args_array, SYSARG_2);
//                LOGE("push_array_of_xpointers_t finish ")
//            }

            break;
        }

The part that goes out. Is the code where I replace the argument of the svc. This code never takes effect. If I open the code for this part of the comment, then, this part of the log that I test inside will not be printed 。

 LOGE("test execve printf ->  %s", buffer); 

If you comment out the code that replaces the execve parameter, then

LOGE("test execve printf -> %s", buffer);

the log is printed completely. I don't know where the problem is, is it because I replaced it in the wrong way?

w296488320 commented 1 year ago

I have invited you to my project through github, and the project name is《 ExecveSvcIntercept 》, and this helloword reproduces my question very well.

michalbednarski commented 1 year ago

That application crashes because it attempts restoring registers after successful execve

Either don't restore registers after execve (example patch, this will crash application if path was changed but execve fails):

diff --git a/app/src/main/cpp/syscall/tracer/exit.cpp b/app/src/main/cpp/syscall/tracer/exit.cpp
index 91c6265..4c77c98 100644
--- a/app/src/main/cpp/syscall/tracer/exit.cpp
+++ b/app/src/main/cpp/syscall/tracer/exit.cpp
@@ -38,6 +38,7 @@ void translate_syscall_exit(Tracer *tracee) {

     switch (syscall_number) {
         case SC_execve: {
+            tracee->restore_original_regs = false;

             break;

Or don't intercept execve exit

diff --git a/app/src/main/cpp/syscall/tracer/seccomp.cpp b/app/src/main/cpp/syscall/tracer/seccomp.cpp
index 3af2f2b..b69f0ed 100644
--- a/app/src/main/cpp/syscall/tracer/seccomp.cpp
+++ b/app/src/main/cpp/syscall/tracer/seccomp.cpp
@@ -480,7 +480,7 @@ static FilteredSysnum proot_sysnums[] = {
         {SC_access, 0},
         {SC_acct, 0},
         {SC_chdir, 0},
-        {SC_execve, FILTER_SYSEXIT},//查找错误使用
+        {SC_execve, 0},//查找错误使用
         {SC_faccessat, 0},
         {SC_faccessat2, 0},
         {SC_fchdir, 0},
w296488320 commented 1 year ago

Yes, I also found that the sign11 is indeed caused by this problem. Thank you again.

w296488320 commented 1 year ago

I am trying to implement sandbox on Android with boot and I tried to simulate a set of proc using fuse but failed. The proot is a great project that solves the problem of many programs.