如何设计一个CobaltStrike的Job管理机制

CobaltStrike的job管理机制,主要是提供给操作者,撤销一些类似耗时等不想继续运行的任务的能力。主要用在长时间的命令执行,大文件的下载等过程中 (注意这个job并不是windows系统自身的job机制)

如何实现

定义数据结构

语言使用c/c++,首先我们需要定义一个结构体,用于保存每个job的状态,其中包含任务id,任务进程id,读写管道句柄等信息。
同时,我们后续要对所有的job进行查找,添加,删除操作,所以我们需要一个数据结构,在高级语言中有很多方式可以实现,在c/c++ 我们可以通过链表的方式来搞定:

typedef struct _JOB_NODE
{
    int jobid;
    int pid
    HANDLE process;
    HANDLE hRead;
    HANDLE hWrite;
    struct _JOB_NODE* next;
} JOB_NODE;

以上就是一个最精简的job状态结构,你也可以加一些自己的定义进去,比如描述信息之类的。

命令执行引入job机制

然后我们看,如何在进程中使用job机制:
首先你要思考,通过job kill掉一个正常长时间运行,并且在不断的输出信息的过程中会设计到哪些东西。
首先读写管道也要涉及到,因为我们关闭管道后,就不会回读取输出,其次进程句柄要涉及到,因为我们要去kill掉这个进程,释放资源,不然进程会一直都在。
代码大致如下:

    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
    HANDLE hRead, hWrite;
    CreatePipe(&hRead, &hWrite, &sa, 0x100000);
    si.hStdInput = NULL;
    si.hStdOutput = hWrite;
    si.hStdError = hWrite;
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    CreateProcessA(NULL,commandline,NULL,NULL,true,CREATE_NO_WINDOW,NULL,NULL,&si,&pi)
    if(waitforsingleobject(pi.hprocess,5000)==WAIT_TIMEOUT)
    {
         Jobadd();
         ........
    }

这里有一个细节,我们创建了进程后,会等待一段时间,因为很多命令执行可能一下就完了,这种我们没必要加入到job管理中,我们只针对那些长时间运行的任务。

添加job

添加job的过程,其实就是在链表中加入节点的一个简单的算法,你可以直接让gpt给你生出一个:

JOB_NODE* GobalJobs = NULL;
JOB_NODE* JobAdd(JOB_NODE* newJob)
{
    static DWORD JobCurrentId = 0;

    JOB_NODE* job = GobalJobs;
    newJob->id = JobCurrentId++;
    if (job)
    {
        while (job->next)
            job = job->next;

        job->next = newJob;
    }
    else
    {
        GobalJobs = newJob;
    }
    return job;
}

这是一个尾插的方式。
但是在插入之前,你要初始化一些job结构体,把需要赋值的变量赋值一下,比如进程句柄,管道句柄。

jobkill

jobkill中所做的事其实就是关闭相关的句柄,结束对应的进程,但是cobaltstike中好像只是关闭了管道的句柄,使得不再打印输出,但进程实际还是运行的,需要等它自己结束。结束完后,再把job从链表中删除。

for (JOB_NODE* job = GobalJobs; job; job = job->next)
{
    if (job->jobid == id)
    {
            TerminateProcess(job->process,0);
            CloseHandle(job->process);
            CloseHandle(job->hRead);
            CloseHandle(job->hWrite);
            JobRemove(id);
     }
}
void JobRemove(int id){
    JOB_NODE* prev = NULL;
    JOB_NODE** pNext;
    for (JOB_NODE* job = GobalJobs; job; job = *pNext)
    {
        if (job->jobid == id)
        {
            prev = job;
            pNext = &job->next;
            continue;
        }
        if (prev)
            pNext = &prev->next;
        else
            pNext = &GobalJobs;
        *pNext = job->next;
        free(job);
    }

}

以上代码都可以让gpt给你搞定,自己修一下就行了。

整体的代码逻辑就是大概这样,文章也只是为了简单说明一下这种job任务管理机制,并且本文也只是涉及到了命令执行这一个方面,像Cobaltstrike中涉及到的文件下次的job,后渗透的一些都没有涉及,不过这些都是大同小异的,可以根据自己的需求来逐步添加!