首页
社区
课程
招聘
[原创]不问年少的CrackMe
发表于: 2017-5-2 08:19 2332

[原创]不问年少的CrackMe

2017-5-2 08:19
2332

注册码说明:原注册码为bwns@pediy!后来根据要求全改为字母加数字,连空格都去掉了!

现注册码为:BwnsAtPediy2017KX9Ok

注册成功截图:


制作思路:

      来源于约瑟夫环的变形处理。将一张码表看成一个圈,取用户输入的字符在码表中的下标。然后从此字符的下一字符处开始移动其下标值/5+5(这样做为了大幅度减少循环次数),然后取对应的字符,对此字符进行高5位低3位移位操作后,取其HEX字符进行验证。

      验证方式为对每个字符的ASCII/4的商,余数比较。(可由此进行注册码的反推。)

其他处理:

1、在Button中进行了特殊处理,使用int3异常开始游戏,必须触发异常,否则就退出了。

2、用TLSOEP进行异或解密。

3、使用了SMC对显示对话框解码,解码的KEY即为用户输入的字符。此处用了try-catch包裹,故输入不正确的key不会崩溃。

4StartGame函数也进行了SMC加密,运行时解密,完成后再加密。

5、对EXE加入简单自校验,使用MapFileCheckSum(测试发现此处360会误报木马),

若发现改动,直接退出。

6SMC均采用IDA直接查看地址后精确处理,无其它标记,制作稍麻烦。

关键代码:

     //置换游戏,找到随机数组中的字母后按下标再继续置换。

        class CZHGame

        {

        public:

                  CZHGame();

                  ~CZHGame();

        public:

                  void StartGame(string strInput);

                  void DecResult();

        protected:

                  bool IsOK();

                  string GetHexKey() const;

                  bool VerifyLength(string strInput);

                  typedef struct _XY

                  {

                           _XY() {}

                           _XY(charx,chary) {this->x =x;this->y =y; }

                           charx, y;

                  }XY, *pXY;

   

        protected:

                  //密文数组,初始化将会被打乱。

                  vector<char> m_vPwd;

                  //玩家输入被置换后的数组

                  vector<char> m_vKey;

                  vector<XY> m_vXY;

                  //用户输入的原始数据(将直接用于SMC加解码)

                  string m_strInput;

        };

        void __stdcall ShowResult() noexcept

        {

                  char key[] = {'B','W','N','S'};

                  char msg[] = {0x15,0x32,0x22,0x3f,0x62,0x33,0x21,0x3d,0x27,0x76,0x74,0x7a,0x42};

                  int i = 0;

                  for_each (begin(msg), end(msg), [&](char& c) {c ^= key[i++ % 4]; });

                  MessageBoxA(NULL, msg,"",MB_ICONINFORMATION);

        }

        CZHGame::CZHGame()

        {

                  m_vPwd.reserve(64);

                  for (char i ='a'; i <='z'; i++)

                  {

                           m_vPwd.push_back(i);

                           m_vPwd.push_back(toupper(i));

                  }

                  m_vPwd.push_back('@');

                  for (char i ='0'; i <='9'; i++)

                           m_vPwd.push_back(i);

                  m_vPwd.push_back('!');

                  reverse(begin(m_vPwd), end(m_vPwd));

                  

                  /*注意此处未初始化随机种子,所以数组中的字符每次是不会变化的*/

                  for (int i = 0; i < 3; i++)

                           random_shuffle(begin(m_vPwd), end(m_vPwd));

                  //字母简单加密处理,全部与0xcc异或

                  for_each (begin(m_vPwd), end(m_vPwd),[](char& c) {c ^= 0xcc; });

                  m_vXY.reserve(40);

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(0, 3));

                  m_vXY.push_back(XY(0, 0));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(0, 1));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 0));

                  m_vXY.push_back(XY(0, 2));

                  m_vXY.push_back(XY(2, 0));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(1, 0));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 1));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 1));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 1));

                  m_vXY.push_back(XY(0, 3));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(0, 3));

                  m_vXY.push_back(XY(1, 2));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(2, 0));

                  m_vXY.push_back(XY(0, 2));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(0, 0));

                  m_vXY.push_back(XY(0, 2));

                  m_vXY.push_back(XY(1, 0));

                  m_vXY.push_back(XY(2, 2));

                  m_vXY.push_back(XY(2, 3));

                  m_vXY.push_back(XY(0, 2));

                  m_vXY.push_back(XY(1, 0));

                  m_vXY.push_back(XY(0, 3));

        }

       void CZHGame::StartGame(string strInput)

        {

                  if(! VerifyLength(strInput))return;

                  

                  //通过长度设定后,先保存输入

                  m_strInput = strInput;

                  int nLen = strInput.length();

                  vector<char> vec(nLen, 0);

                  for_each (begin(strInput), end(strInput), [](char& c) {c ^=  0xcc; });

                  for (int i = 0; i < nLen; i++)

                  {

                           auto it = find(m_vPwd.begin(), m_vPwd.end(),strInput[i]);

                           //从码表中查找用户输入的内容

                           if (it != m_vPwd.end())

                           {

                                    //得到每个字母/数字相对于码表的下标

                                    vec[i] = distance(m_vPwd.begin(), it);

                                    //若是首位的下标,则将其改为1

                                    vec[i] = (vec[i]== 0 ? 1 : vec[i]);

                                    for(int j = 0; j <=i; j++)

                                    {

                                             int k = 0;

                                             while(1)

                                              {

                                                       it++;

                                                       if (it == m_vPwd.end())

                                                                it = m_vPwd.begin();

                                                       k++;

                                                       //以vec[i]/5+5为计数终点。

                                                       if (k == (vec[i]/ 5) + 5)

                                                       {

                                                                vec[i] = distance(m_vPwd.begin(), it);

                                                                vec[i] = (vec[i]== 0 ? 1 : vec[i]);

                                                                break;

                                                       }

                                              }

                                    }

                                    vec[i] = m_vPwd[vec[i]];

                           }

                  }

                  //得到vec所指向的码表字符,再进行处理

                  for_each (begin(vec),end(vec), [](char& c) {

                           char tmp = c^ 0xcc;

                           c = ((tmp >>5) | (tmp <<3));

                  });

                  

                  m_vKey = vec;

        }

        bool CZHGame::VerifyLength(string strInput)

        {

                  return (5 <strInput.length() && strInput.length() <= 20);

        }

        string CZHGame::GetHexKey()const

        {

                  string strTmp(begin(m_vKey), end(m_vKey));

                  return boost::algorithm::hex(strTmp);

        }

        bool CZHGame::IsOK()

        {

                  // BwnsAtPediy2017KX9Ok

                  string strKey = GetHexKey();

                  int nLen = strKey.length();

                  XY xy;

                  vector<char> tmp = {'0','1','2','3','4','5',

                           '6','7','8','9','A','B','C','D','E','F'};

                  vector<XY> vec;

                  vec.reserve(40);

                  for(int i = 0; i < nLen; i++)

                  {

                           auto beg = begin(tmp);

                           auto it = find(beg, end(tmp), strKey[i]);

                           char dis = distance(beg, it);

                           xy.x =dis / 4;

                           xy.y = dis % 4;

                           vec.push_back(xy);

                  }

                  

                  if (vec.size() != m_vXY.size())returnfalse;

                  

                  return (0 == memcmp(&m_vXY[0], &vec[0], m_vXY.size()*sizeof(XY)));

        }

        

        /*编译后查看EXE中ShowResult函数,代码长度为0x94*/

        voidCZHGame::DecResult()

        {

#ifdef__SMC__

                  if(IsOK())

                  {

                           void(__stdcall*pShowResult)() = ShowResult;

                           int i = 0, fnLen = 0x94;

                           LPVOID pFn = VirtualAlloc(0, 0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

                           memcpy_s(pFn,0x1000, pShowResult, fnLen);

                           PBYTE pMyFn = reinterpret_cast<PBYTE>(pFn);

                           try

                           {

                                    /*解码显示结果函数*/

                                    int j = 0;

                                    for(; j < fnLen;)

                                              *pMyFn++^= m_strInput[j++ % m_strInput.length()];

                                    *pMyFn ^=m_strInput[j % m_strInput.length()];

                                    PBYTE pCall =reinterpret_cast<PBYTE>(pFn) + 0x60;

                                    DWORD dwCallAddr = *(DWORD*)(pCall + 1);

                                    /*修正CALL的地址,新地址=原CALL地址+跳转距离新CALL地址-5=原CALL地址+跳转距离-新CALL地址*/

                                    *(DWORD*)(pCall + 1) = dwCallAddr + ((DWORD)pShowResult + 0x60) -reinterpret_cast<DWORD>(pCall);

                                    pCall =reinterpret_cast<PBYTE>(pFn) + 0x8b;

                                    dwCallAddr= *(DWORD*)(pCall + 1);

                                    *(DWORD*)(pCall + 1) = dwCallAddr + ((DWORD)pShowResult + 0x8b) -reinterpret_cast<DWORD>(pCall);

                                    __asmcall pFn;

                           }

                           catch(...){}

                          VirtualFree(pFn, 0x1000,MEM_RELEASE);

                  }

        }

#else

                  //待加密的文件通过IDA查看对应的函数地址和长度,直接进行加密处理

                  /*ShowResult函数地址,此处函数地址编译后可能会改变!*/

                  DWORD fnAddr = 0x11a9c;

                  int fnLen = 0x94;

                  fstream fs("e:\\crackme.exe", ios::in | ios::out | ios::binary);

                  if(fs.is_open())

                  {

                           fs.seekg(0,ios::end);

                           int nLen = (int)fs.tellg();

                           fs.seekg(0,ios::beg);

                           shared_ptr<char> pDat(newchar[nLen], default_delete<char[]>());

                           fs.read(&*pDat,nLen);

                           PBYTE pCur =(PBYTE)&*pDat + fnAddr;

                           string pwd ="BwnsAtPediy2017KX9Ok";

                           int nPwdLen = pwd.length();

                           for(int i = 0; i <fnLen; i++)

                                    *pCur++ ^=pwd[i % nPwdLen];

                           // StartGame函数地址,编译后可能会有改变,用IDA查看并修改!

                           fnAddr = 0x11b30;

                           fnLen = 0x1aa;

                           pCur =(PBYTE)&*pDat + fnAddr;

                           pwd ="PEDIY";

                           nPwdLen =pwd.length();

                           for(inti = 0; i <fnLen; i++)

                                    *pCur++ ^=pwd[i % nPwdLen];

                           PIMAGE_DOS_HEADER pDosHdr =reinterpret_cast<PIMAGE_DOS_HEADER>(&*pDat);

                           PIMAGE_NT_HEADERS pNtHdr =reinterpret_cast<PIMAGE_NT_HEADERS>(&*pDat+ pDosHdr->e_lfanew);

                           DWORD dwOEPRVA =pNtHdr->OptionalHeader.AddressOfEntryPoint;

                           /*VC编译的程序第一个节表就是.text,所以不用再搜索了!*/

                           PIMAGE_SECTION_HEADER pSecText = IMAGE_FIRST_SECTION(pNtHdr);

                           //计算OEP的文件偏移

                           DWORD dwOEP = dwOEPRVA - pSecText->VirtualAddress + pSecText->PointerToRawData;

                           //PBYTE pBeg = reinterpret_cast<PBYTE>(&*pDat + dwOEP), pEnd =reinterpret_cast<PBYTE>(&*pDat + dwOEP + 200);

                           dwOEP =reinterpret_cast<DWORD>(&*pDat + dwOEP);

                           //加密OEP

                           byte smc[] = { 0xeb,0x74, 0x58, 0xcc, 0xe8, 0x75 };

                           EncryptTarget(dwOEP,200, smc, 6);

                           

                           //到头部,准备写入。

                           fs.seekp(0,ios::beg);

                           fs.write(&*pDat,nLen);

                           fs.flush();

                           fs.close();

                           DWORD dwOldCheckSum,dwNewCheckSum;

                           if(CHECKSUM_SUCCESS == MapFileAndCheckSum(L"e:\\crackme.exe", &dwOldCheckSum, &dwNewCheckSum))

                           {

                                    fstream fs("e:\\crackme.exe", ios::in | ios::out | ios::binary);

                                    if(fs.is_open())

                                    {

                                              fs.seekg(0,ios::end);

                                              intnLen = (int)fs.tellg();

                                              fs.seekg(0,ios::beg);

                                              shared_ptr<char> pDat(newchar[nLen], default_delete<char[]>());

                                              fs.read(&*pDat,nLen);

                                              PIMAGE_DOS_HEADER pDosHdr =reinterpret_cast<PIMAGE_DOS_HEADER>(&*pDat);

                                              PIMAGE_NT_HEADERS pNtHdr =reinterpret_cast<PIMAGE_NT_HEADERS>(&*pDat+ pDosHdr->e_lfanew);

                                              pNtHdr->OptionalHeader.CheckSum= dwNewCheckSum;

                                              //到头部,准备写入。

                                              fs.seekp(0,ios::beg);

                                              fs.write(&*pDat,nLen);

                                              fs.flush();

                                              fs.close();

                                    }

                           }

                           MessageBox(NULL,L"加密显示结果函数完成!",L"", MB_ICONINFORMATION);

                  }

        }

#endif

}



在Win7 32位,xp下测试通过!(XP下SetUnhandledExceptionFilter只有一次有效,再次点击就崩溃了,WIN7下无问题,多次点击正常!)

由于OnStart放在CMianDialog中,其又会调用到类中的其它函数,不好处理,所以只能这样了。再说现在大部分应该都是用win7以上了吧。

 // 顶级异常处理
 LONG WINAPI MyTopExceptionFilter(_EXCEPTION_POINTERS*  pept)
 {
  if (pept->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
  {

   // 此处使用PostMessage/SendMessage在xp下会崩溃,异常未处理到。在WIN7下测试正常。
   PostMessage(g_hMainDlg, WM_START_GAME, 0, 0);

   // 跳过PostQuitMessage,直接返回
   pept->ContextRecord->Eip += 9; 
   return EXCEPTION_CONTINUE_EXECUTION;
  }

  // 其它异常不做处理
  return EXCEPTION_CONTINUE_SEARCH;
 }



[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//