先看一张图片和一个函数
推箱子图片 推箱子程序37关 然后我想设计一个函数 参数就是推箱子的路线。
好了 我规定
2进制的
00 向上走一步
01 向下走一步
10 向左走一步
11 向右走一步
刚好4步组成一个byte 假如最后一个byte的前2步刚好走完全程 那么后面的2步都清零 (也就是向上走)
好了 设计函数如下
#define STONE 0
#define EMPTY 1
#define BOX 2
#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3
#define MAX_I 6
#define MAX_J 7
//次函数返回 (100 - 一共花费的步数)
int __stdcall IsRightKey(PBYTE buf, int iLength)
{
int iCountStep = 0;
//棋盘
BYTE bChessBoard[MAX_I][MAX_J] =
{
{STONE, STONE, STONE, EMPTY, EMPTY, EMPTY, STONE},
{STONE, STONE, STONE, BOX, BOX, BOX, STONE},
{STONE, STONE, STONE, EMPTY, EMPTY, EMPTY, STONE},
{EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, BOX, STONE},
{EMPTY, BOX, EMPTY, BOX, EMPTY, EMPTY, EMPTY},
{EMPTY, EMPTY, STONE, EMPTY, STONE, EMPTY, STONE}
};
//当前小孩的位置
int iChild_i = 0;
int iChild_j = 4;
int iPos = 0;
PBYTE pCur = buf;
//遍历取得每一个字节,然后解析每个字节的4小步
while(iPos < iLength)
{
BYTE bTemp = *(pCur + iPos);
//分解成4小步
BYTE b4[4] = {0,0,0,0};
b4[0] = (bTemp>>6) & 0x03;
b4[1] = (bTemp>>4) & 0x03;
b4[2] = (bTemp>>2) & 0x03;
b4[3] = bTemp & 0x03;
//一共是4步
for (int i=0; i<4; i++)
{
switch(b4[i])
{
case UP:
//先判断是否越界
if (iChild_i == 0)
{
return 0;
}
//如果是石头
if (bChessBoard[iChild_i-1][iChild_j] == STONE)
{
return 0;
}
//如果是空地
if (bChessBoard[iChild_i-1][iChild_j] == EMPTY)
{
iChild_i--;
break;
}
//现在肯定是箱子了
//判断箱子上面是否是边界
if (iChild_i-1 == 0)
{
return 0;
}
//判断箱子上方是否有空位置
if (bChessBoard[iChild_i-2][iChild_j] != EMPTY)
{
return 0;
}
else
{
bChessBoard[iChild_i-2][iChild_j] = BOX;
bChessBoard[iChild_i-1][iChild_j] = EMPTY;
iChild_i--;
break;
}
__asm int 3;
break;
case DOWN:
//先判断是否越界
if (iChild_i == MAX_I-1)
{
return 0;
}
//如果是石头
if (bChessBoard[iChild_i+1][iChild_j] == STONE)
{
return 0;
}
//如果是空地
if (bChessBoard[iChild_i+1][iChild_j] == EMPTY)
{
iChild_i++;
break;
}
//现在肯定是箱子了
//判断箱子下面是否是边界
if (iChild_i+1 == MAX_I-1)
{
return 0;
}
//判断箱子下方是否有空位置
if (bChessBoard[iChild_i+2][iChild_j] != EMPTY)
{
return 0;
}
else
{
bChessBoard[iChild_i+2][iChild_j] = BOX;
bChessBoard[iChild_i+1][iChild_j] = EMPTY;
iChild_i++;
break;
}
__asm int 3;
break;
case LEFT:
//先判断是否越界
if (iChild_j == 0)
{
return 0;
}
//如果是石头
if (bChessBoard[iChild_i][iChild_j-1] == STONE)
{
return 0;
}
//如果是空地
if (bChessBoard[iChild_i][iChild_j-1] == EMPTY)
{
iChild_j--;
break;
}
//现在肯定是箱子了
//判断箱子左面是否是边界
if (iChild_j-1 == 0)
{
return 0;
}
//判断箱子左方是否有空位置
if (bChessBoard[iChild_i][iChild_j-2] != EMPTY)
{
return 0;
}
else
{
bChessBoard[iChild_i][iChild_j-2] = BOX;
bChessBoard[iChild_i][iChild_j-1] = EMPTY;
iChild_j--;
break;
}
__asm int 3;
break;
case RIGHT:
//先判断是否越界
if (iChild_j == MAX_J-1)
{
return 0;
}
//如果是石头
if (bChessBoard[iChild_i][iChild_j+1] == STONE)
{
return 0;
}
//如果是空地
if (bChessBoard[iChild_i][iChild_j+1] == EMPTY)
{
iChild_j++;
break;
}
//现在肯定是箱子了
//判断箱子右面是否是边界
if (iChild_j+1 == MAX_J-1)
{
return 0;
}
//判断箱子右方是否有空位置
if (bChessBoard[iChild_i][iChild_j+2] != EMPTY)
{
return 0;
}
else
{
bChessBoard[iChild_i][iChild_j+2] = BOX;
bChessBoard[iChild_i][iChild_j+1] = EMPTY;
iChild_j++;
break;
}
__asm int 3;
break;
default:
__asm int 3;
break;
}
//检测是否OK了
iCountStep++;
if (bChessBoard[3][3] == BOX &&
bChessBoard[4][2] == BOX &&
bChessBoard[4][4] == BOX &&
bChessBoard[4][6] == BOX &&
bChessBoard[5][3] == BOX &&
bChessBoard[5][5] == BOX)
{
for (int j=i+1; j<4; j++)
{
if (b4[j] != 0)
{
return 0;
}
}
if (iLength == iPos+1)
{
return 100-iCountStep;
}
else
return 0;
}
}
iPos++;
}
return 0;
}
这是核心判断代码了 如果写个简单VM把这个函数处理一下 难度会大上n倍
下面讲述如何假如简单 防爆破 我用了简单的CRC32做校验算法。
我想的方法很弱 得用OD自己patch 记得海风大侠有个cm防爆 不用OD patch 直接代码编译出来的
CRC32 算法代码。
unsigned int GetCrc32(char* InStr,unsigned int len)
{
//生成Crc32的查询表
unsigned int Crc32Table[256] = {0};
unsigned int Crc;
for (unsigned int i = 0; i < 256; i++)
{
Crc = i;
for (unsigned int j = 0; j < 8; j++)
{
if (Crc & 1)
Crc = (Crc >> 1) ^ 0xEDB88320;
else
Crc >>= 1;
}
Crc32Table[i] = Crc;
}
//开始计算CRC32校验值
Crc = 0xFFFFFFFF;
for(i=0; i<len; i++)
{
Crc = (Crc >> 8) ^ Crc32Table[(Crc & 0xFF) ^ (BYTE)InStr[i]];
}
Crc ^= 0xFFFFFFFF;
return Crc;
}
下面是ok按钮的函数
BYTE g_szCaption[100] = {0xcc,0xe1,0xca,0xbe, 0x00}; //需要用crc1 来异或 用OD自己patch
BYTE g_szText[100] = {0xB9,0xA7,0xCF,0xB2,0xB9,0xFD,0xB9,0xD8,0x00}; //需要用crc2 来异或 用OD自己patch
//5943d297ea29cf30d5424094 这是一个合理的路径 也可以自己找一个
void CCrackMeDlg::OnOK()
{
DWORD dwFucAddr = 0x12345678; //这个地址需要修改为导入表MessageBoxIndirectA的地址 需要自己用LordPE 查 然后自己patch
int i=0;
int iResult = 0;
int iKeyLength = 0;
char szKey[1024] = {0};
BYTE bKey[512] = {0};
MSGBOXPARAMSA msg;
ZeroMemory(&msg, sizeof(msg));
msg.cbSize = sizeof(MSGBOXPARAMSA);
msg.lpszCaption = (LPSTR)g_szCaption;
msg.lpszText = (LPSTR)g_szText;
MessageBoxIndirectA(&msg); //这里调用一下只是为了让导入表里有此函数 需要自己patch掉
DWORD dwCrcSubFuction = 0;
DWORD dwCrcJudge = 0;
GetDlgItemText(IDC_EDIT1, szKey, 1024);
iKeyLength = lstrlen(szKey);
if (iKeyLength <= 4 || iKeyLength >= 1000 || iKeyLength%2 != 0)
{
goto __error;
}
for (i=0; i<iKeyLength; i++)
{
if ((szKey[i] >= 'A' && szKey[i] <= 'F') || (szKey[i] >= '0' && szKey[i] <= '9') || (szKey[i] >= 'a' && szKey[i] <= 'z'))
{
__asm nop
}
else
goto __error;
}
__Judge_s:
StringToHex(szKey, bKey);
iResult = IsRightKey(bKey, iKeyLength/2);
if (iResult == 0)
{
goto __error;
}
__asm
{
mov eax, offset __Judge_s
mov dwCrcJudge, eax
}
//跟踪到这里 记住俩crc值 需要调试找到这俩值
dwCrcJudge = GetCrc32((char *)dwCrcJudge, 0x36); //其实这里直接把OK按钮开始到 本句的地址 做个crc就更好了
dwCrcSubFuction = GetCrc32((char *)IsRightKey+1, 0x300);
*(DWORD*)g_szCaption ^= dwCrcJudge;
*(DWORD*)g_szText ^= dwCrcSubFuction;
*(DWORD*)(g_szText+4) ^= dwCrcSubFuction;
dwFucAddr ^= dwCrcJudge;
dwFucAddr ^= dwCrcSubFuction;
dwFucAddr = *(DWORD*)dwFucAddr; //解密得到真正的api地址
dwCrcJudge = (DWORD)&msg;
__asm
{
push dwCrcJudge
call dwFucAddr
}
__error:
__asm nop
}
如果您有什么好的 防爆破 思路欢迎 一起跟帖讨论。