iOS 自定义命令行工具

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

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

一、工程创建

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

#import 
#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 
/**
 @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 中。

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 

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 自定义命令行工具)