窥见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
沙发支持下 完全不会的~:'(weeqw 精品文章啊 本帖最后由 josong 于 2009-10-25 12:16 编辑
完全看不懂...
ReplaceCode这词最近常见, ..到底他是什么..
跟一些文章讲的"被偷代码"有什么不一样呢?
请高人指点.. 5# josong
下载我提供的实例,参照文章中给出的ReplaceCoceFunc的地址,自己跟进去看看,你就知道ReplaceCode长相是啥样子了 很直接的分析,
很精彩的代碼. 还是这个论坛排版比较好,比begin09的代码舒服多了。
页:
[1]