跳转至

钩住 RTMPS 的 decode() 函数

我命名为 decode() 的函数(用于解密传入数据)也是一个在循环中调用的 __thiscall。它以 4,096 字节的块为单位操作,把 buffer 和 size 作为参数。循环要复杂得多,包含多个函数调用、嵌套循环和循环退出,但钩住它的工作方式与钩住所谓的 encode() 函数非常相似。复杂度增加的原因与钩住函数无关,但它让代码难以总结,所以我不会在这里展示原始函数。底线是:一旦所有复杂度被抹去,decode() 循环就是反转的 encode()循环。

再一次,我设计了一个两部分近调用钩子。第一部分 reportDecode() 在这里展示:

void __stdcall reportDecode(const unsigned char* 
buffer, unsigned int size)
{
    printBuffer(buffer, size);
}

这个函数记录通过的每个数据包。我当时没有循环索引,所以我决定记录每一个部分数据包也是可以的。

钩子的第二部分 myDecode() 函数充当新被调用者,做所有脏活,如下所示:

void __declspec(naked) myDecode()
{
    __asm {
        MOV EAX, DWORD PTR SS:[ESP + 0x4] // 获取 

buffer

        MOV EDX, DWORD PTR SS:[ESP + 0x8] // 获取 

size

        PUSH EDX // 压入 size
        PUSH EAX // 压入 buffer
        CALL [origDecodeFunc]
        MOV EDX, DWORD PTR SS:[ESP + 0x4] // 获取 

buffer

        PUSH EAX // 存储 eax(返回值)
        PUSH ECX // 存储 ecx
        PUSH EAX // 压入 size
        PUSH EDX // 压入 buffer
        CALL reportDecode // 现在报告结果
        POP ECX // 恢复 ecx
        POP EAX // 恢复 eax(返回值)
        RETN 8 // 返回并清理栈
    }
}

我知道缓冲区被就地解密,意味着 decode() 调用完成,加密块会被解密块覆盖。这意味着 myDecode() 必须在调用 reportDecode() 函数之前调用原始 decode() 函数①,这会给出解码的结果。最终,myDecode() 还需要以原始 decode() 函数相同的值返回并清理栈,最后的 POP②和 RETN③指令处理了这些。