美文网首页
iOS 自定义命令行工具

iOS 自定义命令行工具

作者: HotPotCat | 来源:发表于2021-05-31 16:54 被阅读0次

    我们再越狱手机上能用很多工具,尤其是在终端上的一些操作。那么怎么实现一个在iOS终端的命令行工具呢?

    比如我们将常用的命令封装成自己的一个命令行工具方便自己调用。在这里我以ps -Adebugserver的开启为例。

    一、工程创建

    首先用Xcode创建一个iOS App,这么做是因为要生成iOS终端可执行的命令行,默认main函数如下:

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    

    这样工程就创建好了,接下来就是功能的实现了。当然可以根据自己的需要配置自己支持的架构等相关内容。

    二、main函数

    2.1 main函数精简

    由于是制作命令行工具,所以界面相关的内容都删除,只保留main函数。精简后如下:

    #import <Foundation/Foundation.h>
    /**
     @param argc 入参个数
     @param argv 入参数组  argv[0] 为可执行文件
     */
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            //根据自己的需要做逻辑处理
        }
        return 0;
    }
    

    2.2 main框架

    首先实现基本的框架,我们需要的功能一个是列出所有进程,一个是启动手机端debugserver。后续可能还会扩展更多功能并且为了方便使用需要加入一个help和容错处理。那么就有了:

    • help函数提供说明帮助。
    • runPS实现ps -A列出所有进程。
    • runDebugServer实现开启手机端runDebugServer功能。

    实现代码如下:

    /**
     @param argc 入参个数
     @param argv 入参数组  argv[0] 为可执行文件
     */
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            if (argc == 1 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
                //help();
            } else {
                if (strcmp(argv[1], "-p") == 0 || strcmp(argv[1], "--process") == 0) {
                    runPS();
                } else if ((strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--debugserver") == 0) && argc > 2 && argv[2] != NULL) {
                    runDebugServer(argv[2]);
                } else {
                    printf("illegal option:%s\n",argv[1]);
                    printf("Try 'HPCMD --help' for more information. \n");
                }
            }
        }
        return 0;
    }
    

    main函数有两个参数:

    • argc:参数个数。
    • argv:入参数组,这个入参数组第一个参数argv[0]就是可执行文件本身。

    三、如何代码调用shell命令。

    查询资料得知有3种方式:

    • 1.system函数,目前已经被废弃。不过应该可以找到函数地址去尝试直接调用。
      1. NSTask,不过这个只能用在macOS中,如果写macOS终端命令行工具可以用这个。
      1. posix_spawn目前也只有这个能用了。在#include <spawn.h>中。

    posix_spawn函数定义如下:

    int     posix_spawn(pid_t * __restrict, const char * __restrict,
        const posix_spawn_file_actions_t *,
        const posix_spawnattr_t * __restrict,
        char *const __argv[__restrict],
        char *const __envp[__restrict]) __API_AVAILABLE(macos(10.5), ios(2.0)) __API_UNAVAILABLE(watchos, tvos);
    

    posix_spawn函数一共6个参数

    • pid_t:子进程pidpid 参数指向一个缓冲区,该缓冲区用于返回新的子进程的进程ID
    • const char * :可执行文件的路径path(其实就是可以调用某些系统命令,只不过要指定其完整路径)
    • posix_spawn_file_actions_tfile_actions 参数指向生成文件操作对象,该对象指定要在子对象之间执行的与文件相关的操作
    • posix_spawnattr_tattrp 指向一个属性对象,该对象指定创建的子进程的各种属性。
    • argv:指定在子进程中执行的程序的参数列表
    • envp:指定在子进程中执行的程序的环境

    这里简单封装runCMD函数如下:

    /*
     posix_spawn 函数一共6个参数
     pid_t:子进程 pid(pid 参数指向一个缓冲区,该缓冲区用于返回新的子进程的进程ID)
     const char * :可执行文件的路径 path(其实就是可以调用某些系统命令,只不过要指定其完整路径)
     posix_spawn_file_actions_t:file_actions 参数指向生成文件操作对象,该对象指定要在子对象之间执行的与文件相关的操作
     posix_spawnattr_t:attrp 指向一个属性对象,该对象指定创建的子进程的各种属性。
     argv:指定在子进程中执行的程序的参数列表
     envp:指定在子进程中执行的程序的环境
     */
    #include <spawn.h>
    
    int runCMD(char *cmd, char *argv[]) {
        pid_t pid;
        //这里注意 cmd 也要包含在 argv[0]中传入。
        posix_spawn(&pid, cmd, NULL, NULL, argv, NULL);
        int stat;
        waitpid(pid,&stat,0);
        printf("run cmd:%s stat:%d\n",cmd,stat);
        return stat;
    }
    

    四、功能实现

    4.1 help实现

    //打印help信息
    void help() {
        printf("-p:--process 显示进程 (等效ps -A) \n");
        printf("-d:<--debugserver 应用名称/进程id>开启debugserver (等效 debugserver localhost:12346 -a 进程名/进程id) \n");
        printf("-h:--help \n");
    }
    

    4.2 runPS实现

    void runPS() {
        char *CMD_argv[] = {
           "/usr/bin/ps",
           "-A",
           NULL
        };
       //ps -A
       runCMD(CMD_argv[0],CMD_argv);
    }
    

    4.2 runDebugServer 实现

    //debugserver localhost:12346 -a 进程名
    void runDebugServer(char *process) {
        printf("process:%s\n",process);
        char *CMD_argv[5] = {
           "/usr/bin/debugserver",
           "localhost:12346",
           "-a",
           NULL,
           NULL
        };
       CMD_argv[3] = process;
       runCMD(CMD_argv[0],CMD_argv);
    }
    

    这里需要注意的是最后一个参数要为NULL

    这样整个功能就全部完成。

    五、运行

    1.由于创建的是App工程,编译生成App后将其中的MachO文件拷贝出来。
    2.将可执行文件拷贝到手机根目录

    scp -P 12345 ./HPCMD root@localhost:~/
    

    3.手机端执行HPCMD
    -h:

    zaizai:~ root# ./HPCMD -h
    -p:--process 显示进程 (等效ps -A)
    -d:<--debugserver 应用名称/进程id>开启debugserver (等效 debugserver localhost:12346 -a 进程名/进程id)
    -h:--help
    

    -p:

    zaizai:~ root# ./HPCMD -p
      PID TTY           TIME CMD
        1 ??        17:09.03 /sbin/launchd
      295 ??         5:41.90 /usr/libexec/substituted
      296 ??         0:00.00 (amfid)
     1585 ??         0:00.00 /usr/libexec/amfid
     1600 ??       412:41.57 /usr/sbin/mediaserverd
    

    -d:

    zaizai:~ root# ./HPCMD -d WeChat
    process:WeChat
    debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-1200.2.12
     for arm64.
    Attaching to process WeChat...
    Listening to port 12346 for a connection from localhost...
    

    -s:

    zaizai:~ root# ./HPCMD -s
    illegal option:-s
    Try 'HPCMD --help' for more information.
    

    这样就验证完整个cmd的功能了。

    可以根据自己的需求实现自己的自定义命令行工具,当然对于一些其它操作需要更多权限可以直接导出系统的SpringBoard可执行文件从而导出它的权限文件用ldid重签自己的命令行工具。相关操作可以参考越狱环境debugserver

    参考:
    代码调用shell命令

    相关文章

      网友评论

          本文标题:iOS 自定义命令行工具

          本文链接:https://www.haomeiwen.com/subject/fpgvsltx.html