nick.coton 发表于 2009-10-24 15:48

窥见ACPR1.32的ReplaceCode

【文章标题】: 窥见ACPR1.32的ReplaceCode
【文章作者】: nick
【作者主页】: www.begin09.com
【作者声明】: 成长的足迹~
--------------------------------------------------------------------------------
【详细过程】
ACPR1.32的ReplaceCode,是将原程序中的5个字节的指令替换成CALL ReplaceCodeFun(call指令正好占用5字节),
在ReplaceCodeFun中动态还原被替换的5个字节,同时参杂5个字节的垃圾指令,然后转去执行被还原的代码。
被替换的5字节正常指令呈现出"2+3"或"3+2"的规律,即有2条正常的指令,一条是2字节,一条是3字节。
被参杂的5字节垃圾指令,2字节的垃圾指令通常有2种,ADD REG1,REG2/SUB REG1,REG2垃圾指令对和XOR REG1/REG2垃圾指令对。

在修复ReplaceCode的时候存在2张表:
表一:被加密过的ReplaceCode的字节码表。该表10个字节为一项,正好对应解码出来的10字节指令(正常5字节+垃圾5字节)。
表二:存放被ReplaceCode的代码的下一条指令的偏移地址(有点拗口),实际这个表中的项的值-5就是被ReplaceCode代码的起始偏移地址。


壳区段.perplex作用:
1 部分ReplaceCode将动态解码到壳的区段然后转去执行
2 部分资源(图标,图标组)被抽取到了壳区段当中
完美修复ReplaceCode所替换的代码后,还要将资源目录相关项修复,才能删除壳区段。

示例中ReplaceCodeFun地址:00480416
部分修复代码如下,完整代码和例子见附件

//======================================================
//    函数描述:搜索匹配的二字节垃圾代码的位置
//    参数描述:pStart             搜索的起始位置
//            nRelativeStartPos返回时用于存放找到的垃圾代码相对于起始位置的位置
//            cByte1             要搜索的垃圾代码的第一个字节
//            cByte2             要搜索的垃圾代码的第二个字节
//    返回值:搜索成功 true / 搜索失败 false
//======================================================
bool Search2BytesOpCode( PVOID pStart, int& nRelativeStartPos, unsigned char cByte1, unsigned char cByte2 )
{
        bool bRetCode = false;
        nRelativeStartPos = 0;

        while ( true )
        {
                int nOpCodeSize = GetOpCodeSize( pStart );

                if ( 1 == nOpCodeSize && 0xc3 == ((unsigned char*)pStart) )
                {
                        break;
                }
                else if ( 2 == nOpCodeSize
                                && cByte1 == ((unsigned char*)pStart)
                                && cByte2 == ((unsigned char*)pStart) )
                {
                        bRetCode = true;
                        break;
                }
                //下一个起始搜索位置增加一个OpCodeSize
                pStart = (char*)pStart + nOpCodeSize;
                //记录当前位置距首次搜索起始位置的偏移
                nRelativeStartPos += nOpCodeSize;
        }
        return bRetCode;
}

//主函数
int main(int argc, char* argv[])
{
      DWORD dwPos = 0;
      DWORD dwCount = 0;
      unsigned char szBuf = { 0 };
      DWORDdwBufPos = 0;
      int i = 0;
        int j = 0;
        int k = 0;
        int nOpCodeSize = 0;
       
        //将被加密的ReplaceCode循环解密
      for ( i = 0; i < nReplaceCodeItems * 10; i++ )
      {
                *( ( (unsigned char *)CryptTable ) + i ) ^= CryptKey;
      }

      for ( i=0; i < nReplaceCodeItems; i++ )
      {
                dwPos = 0;
                dwBufPos = 0;
                j = 0;
                nOpCodeSize = 0;
                memset( szBuf, 0x90, sizeof( szBuf ) );

                while ( true )
                {
                        nOpCodeSize = GetOpCodeSize( ( LPVOID )&CryptTable );

                        //如果当前指令是retn指令,则退出本次修正
                        if ( 1 == nOpCodeSize && 0xc3 == CryptTable )
                        {
                                for ( ; j < 10; j++ )
                                {
                                        CryptTable = 0x90;
                                }
                                break;
                        }
                        //一个字节的指令全部是垃圾指令,直接nop
                        else if ( 1 == nOpCodeSize )
                        {
                                CryptTable = 0x90;
                        }
                        //2字节的指令继续判断是否是垃圾指令
                        else if ( 2 == nOpCodeSize )
                        {
                                int nRelativeStartPos = 0;
                                //搜索add/sub垃圾指令对并nop
                                if ( 0x03 == CryptTable || 0x2b == CryptTable )
                                {
                                        if ( Search2BytesOpCode( (LPVOID)&CryptTable,
                                                                                       nRelativeStartPos,
                                                                                       0x2e - CryptTable,
                                                                                       CryptTable ) )
                                        {
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                        }
                                }
                                //搜索xor垃圾指令并nop
                                if ( 0x33 == CryptTable )
                                {
                                        if ( Search2BytesOpCode( (LPVOID)&CryptTable,
                                                                                       nRelativeStartPos,
                                                                                       CryptTable,
                                                                                       CryptTable ) )
                                        {
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                                CryptTable = 0x90;
                                        }
                                }
                        }
                        //调整下条指令的起始点
                        j += nOpCodeSize;
                }
               
                j = 0;
                int k = 0;
                //循环拷贝正常指令到临时buffer
                for ( ; j < sizeof(szBuf); j++ )
                {
                        if ( 0x90 != CryptTable )
                        {
                                szBuf = CryptTable;
                        }
                }
                //将临时buffer的正常指令拷贝回原加密表
                memcpy( (LPVOID)&CryptTable, szBuf, sizeof(szBuf) );               
      }

        //处理文件
        HANDLE hOrigFile = NULL;
        HANDLE hNewFile = NULL;
        HANDLE hMapFile = NULL;
        DWORD dwMappedAddr = 0;
        DWORD dwAddr = 0;
        DWORD dwFileSize = 0;
        char* pWriteBuf = NULL;
        DWORD dwTemp = 0;

        hOrigFile = CreateFile( strFileName,
                                                        GENERIC_READ | GENERIC_WRITE,
                                                        FILE_SHARE_READ,
                                                        NULL,
                                                        OPEN_EXISTING,
                                                        FILE_ATTRIBUTE_NORMAL,
                                                        NULL
                                                   );
        if ( INVALID_HANDLE_VALUE == hOrigFile )
        {
                printf( "ErrCode:%d\n", GetLastError() );
                goto Exit0;
        }

        hNewFile = CreateFile( strNewFileName,
                                                   GENERIC_READ | GENERIC_WRITE,
                                                   FILE_SHARE_READ,
                                                      NULL,
                                                   CREATE_ALWAYS,
                                                   FILE_ATTRIBUTE_NORMAL,
                                                   NULL
                                                );
        if ( INVALID_HANDLE_VALUE == hNewFile )
        {
                printf( "ErrCode:%d\n", GetLastError() );
                goto Exit0;
        }       
       
        dwFileSize = GetFileSize( hOrigFile, 0 );

        pWriteBuf = new char[ dwFileSize ];
        if ( !pWriteBuf )
        {
                goto Exit0;
        }
        memset( pWriteBuf, 0, dwFileSize );

        ReadFile( hOrigFile, pWriteBuf, dwFileSize, &dwTemp, 0 );
        if ( dwFileSize != dwTemp )
        {
                goto Exit0;
        }
        //循环修改数据
        for ( i = 0; i < 1842; i++ )
        {
                dwAddr = PatchOffset + (unsigned long)(LPVOID)pWriteBuf - 5;
                for ( j = 0; j < 5; j++ )
                {
                        *( (char*)dwAddr + j ) = CryptTable;
                }
        }

        WriteFile( hNewFile, pWriteBuf, dwFileSize, &dwTemp, 0 );
        if ( dwFileSize != dwTemp )
        {
                goto Exit0;
        }

Exit0:
        if ( hNewFile )
        {
                CloseHandle( hNewFile );
        }
        if ( hMapFile )
        {
                CloseHandle( hMapFile );
        }
        if ( hOrigFile )
        {
                CloseHandle( hOrigFile );
        }
        system( "PAUSE" );
      return 0;
}


--------------------------------------------------------------------------------
【版权声明】: 本文原创于天草软件安全培训论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年10月24日 15:11:25

wangkui 发表于 2009-10-24 16:26

沙发支持下

小糊涂虫 发表于 2009-10-24 17:02

完全不会的~:'(weeqw

疾风之翼 发表于 2009-10-25 11:15

精品文章啊

josong 发表于 2009-10-25 12:14

本帖最后由 josong 于 2009-10-25 12:16 编辑

完全看不懂...
ReplaceCode这词最近常见, ..到底他是什么..
跟一些文章讲的"被偷代码"有什么不一样呢?
请高人指点..

nick.coton 发表于 2009-10-25 13:33

5# josong

下载我提供的实例,参照文章中给出的ReplaceCoceFunc的地址,自己跟进去看看,你就知道ReplaceCode长相是啥样子了

ZeNiX 发表于 2009-10-26 13:45

很直接的分析,
很精彩的代碼.

datochan 发表于 2009-11-1 17:19

还是这个论坛排版比较好,比begin09的代码舒服多了。
页: [1]
查看完整版本: 窥见ACPR1.32的ReplaceCode