【文章标题】: 窥见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