首页
论坛
课程
招聘
雪    币: 1780
活跃值: 活跃值 (47)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝

[原创]窥见ACPR1.32的ReplaceCode

2009-10-24 15:36 5451

[原创]窥见ACPR1.32的ReplaceCode

2009-10-24 15:36
5451
【文章标题】: 窥见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)[0] ) 
      {
        break;
      }
      else if ( 2 == nOpCodeSize 
          && cByte1 == ((unsigned char*)pStart)[0] 
          && cByte2 == ((unsigned char*)pStart)[1] )
      {
        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[10] = { 0 };
      DWORD  dwBufPos = 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[i][j] );
  
        //如果当前指令是retn指令,则退出本次修正
        if ( 1 == nOpCodeSize && 0xc3 == CryptTable[i][j] )
        {
          for ( ; j < 10; j++ )
          {
            CryptTable[i][j] = 0x90;
          }
          break;
        }
        //一个字节的指令全部是垃圾指令,直接nop
        else if ( 1 == nOpCodeSize )
        {
          CryptTable[i][j] = 0x90;
        }
        //2字节的指令继续判断是否是垃圾指令
        else if ( 2 == nOpCodeSize )
        {
          int nRelativeStartPos = 0;
          //搜索add/sub垃圾指令对并nop
          if ( 0x03 == CryptTable[i][j] || 0x2b == CryptTable[i][j] )
          {
            if ( Search2BytesOpCode( (LPVOID)&CryptTable[i][j+2], 
                         nRelativeStartPos, 
                         0x2e - CryptTable[i][j], 
                         CryptTable[i][j+1] ) )
            {
              CryptTable[i][j] = 0x90;
              CryptTable[i][j+1] = 0x90;
              CryptTable[i][j+2+nRelativeStartPos] = 0x90;
              CryptTable[i][j+3+nRelativeStartPos] = 0x90;
            }
          }
          //搜索xor垃圾指令并nop
          if ( 0x33 == CryptTable[i][j] )
          {
            if ( Search2BytesOpCode( (LPVOID)&CryptTable[i][j+2], 
                         nRelativeStartPos, 
                         CryptTable[i][j], 
                         CryptTable[i][j+1] ) )
            {
              CryptTable[i][j] = 0x90;
              CryptTable[i][j+1] = 0x90;
              CryptTable[i][j+2+nRelativeStartPos] = 0x90;
              CryptTable[i][j+3+nRelativeStartPos] = 0x90;
            }
          }
        }
        //调整下条指令的起始点
        j += nOpCodeSize;
      }
      
      j = 0;
      int k = 0;
      //循环拷贝正常指令到临时buffer
      for ( ; j < sizeof(szBuf); j++ )
      {
        if ( 0x90 != CryptTable[i][j] )
        {
          szBuf[k++] = CryptTable[i][j];
        }
      }
      //将临时buffer的正常指令拷贝回原加密表
      memcpy( (LPVOID)&CryptTable[i][0], 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[i] + (unsigned long)(LPVOID)pWriteBuf - 5;
      for ( j = 0; j < 5; j++ )
      {
        *( (char*)dwAddr + j ) = CryptTable[i][j];
      }
    }
  
    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

[公告]看雪论坛2020激励机制上线了!多多参与讨论可以获得积分快速升级?

上传的附件:
最新回复 (3)
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liyongan 活跃值 2009-12-2 16:25
2
0
长知识了,感谢!
雪    币: 240
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
skylly 活跃值 4 2009-12-3 14:20
3
0
嗯,  事后补救的方法也相当不错 ,  不过我比较喜欢事前的...
雪    币: 326
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Squn 活跃值 2010-9-15 19:02
4
0
nick大牛啊,膜拜!
游客
登录 | 注册 方可回帖
返回