文章圖檔未能上傳檢視原文請移步--公衆賬号:極安禦信安全研究院
作者:selph
目錄:
• 041-genocidel1
• 042-crackme2
• 043-riijj_cm_200411213
• 044-tsrh-crackme4
• 045-CyTom-crackme5
• 046-keyme16
• 047-surre7
• 048-monkeycrackme18
• 049-THraw-crackme89
• 050-daxxor10
參考資料
– [1] WM_INITDIALOG消息 (Winuser.h) - Win32 apps | Microsoft Docs
-
046-keyme1
算法難度:⭐⭐
爆破難度:⭐
資訊收集
運作情況:
真難得見到一次控制台程式:
查殼與脫殼:
還帶殼,ESP定律走起
調試分析:
這裡拿了一堆結構裡的東西在做計算,直接F5看吧,友善一點:
剛開始拿結構裡的東西計算了一個值,然後判斷輸入是否等于這個值,如果等于就成功
這個VersionInformation結構體變量是在上面那個call401390裡填充的:
注冊機
注冊碼生成算法:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#pragma warning(disable: 4996)
int main()
{
OSVERSIONINFOA VersionInformation={0};
VersionInformation.dwOSVersionInfoSize = 148;
GetVersionExA(&VersionInformation);
int key = VersionInformation.dwBuildNumber
+ VersionInformation.dwBuildNumber
+ VersionInformation.dwMajorVersion * VersionInformation.dwMinorVersion
- VersionInformation.dwMinorVersion
+ 3293 * VersionInformation.dwBuildNumber;
printf("%d",key);
system("pause");
}
效果:
-
047-surre
算法難度:⭐⭐
爆破難度:⭐
資訊收集
運作情況:
點選按鈕會彈出打開檔案的框
查殼與脫殼:
調試分析
驗證邏輯很簡潔,就兩件事:讀取檔案周遊每一個字元累加起來,判斷累加和是否是20A9,是的話,表示驗證成功,否則是失敗
注冊機
注冊碼生成算法:
#include
int main()
{
char serial[200] = { 0 };
const int key = 0x20A9;
for (int i = 0; i < key / 0x30; i++) serial[i] = '0';
for (int i = 0; i < key % 0x30; i++) serial[i] += 1;
std::cout << serial;
}
效果:
-
048-monkeycrackme1 算法難度:⭐⭐⭐ 爆破難度:⭐
資訊收集
運作情況:
查殼與脫殼:
調試分析
程式驗證邏輯很簡單:
首先以寫死0xce6d和0x58bf建立了一個對象結構,然後讀取Name,計算一個字元串,然後讀取Serial字元串,進行比對,一樣則表示成功,否則表示失敗
這裡要注意,delphi使用的是32位的fastcall,傳參順序是eax,edx,ecx,棧,最後調用計算字元串的函數的時候,有一個棧中的參數
計算邏輯也很簡單:
首先儲存變量,初始化輸出緩沖區
然後計算Name長度,周遊每一個字元
對于每一個字元,和兩位元組變量右移8位後的結果異或一下,然後轉換成十六進制(大寫)拼接到輸出緩沖區裡
然後中間處理了一下兩位元組值,初值是4DE1是參數傳入的,修改方式是使用異或後的一位元組,加上原本的兩位元組值,然後乘以安全對象的第一個成員,最後加上安全對象的第二個成員的值(第一個成員的值和第二個成員的值可以通過動态調試得知,是固定值)
注冊機
注冊碼生成算法:
#define _CRT_SECURE_NO_WARNINGS
#include
typedef struct _TSecurity
{
_TSecurity(uint16_t a, uint16_t b) :vul1(a), vul2(b) {}
uint16_t vul1;
uint16_t vul2;
}TSecurity,*PTSecurity;
int main()
{
TSecurity obj(0xce6d,0x58bf);
char name[100] = { 0 };
short num = 0x4de1;
char serial[100] = { 0 };
char tmp[100] = { 0 };
std::cin >> name;
int len = strlen(name);
for (int i = 0; i < len; i++)
{
uint8_t c = name[i] ^ (num >> 8);
num = (c + num) * obj.vul1 + obj.vul2;
sprintf(tmp, "%02X", c);
strcat(serial, tmp);
}
std::cout << serial;
}
效果:
-
049-THraw-crackme8 算法難度:⭐⭐ 爆破難度:⭐ 資訊收集 運作情況:
查殼與脫殼:
UPX殼,ESP定律即可
調試分析
邏輯很簡單,首先擷取Name,然後處理一下
處理方式就是把每個字元的ascii轉換成大寫十六進制,然後拼接起來
然後接下來使用一個全局變量,轉換成字元串,然後再這個字元串之後拼接剛剛name轉換的字元串,就是真碼了
最後讀取Serial,進行對比是否是真碼,進行跳轉
注冊機
注冊碼生成算法:
var Serial = "1007689728";
var Name = Console.ReadLine();
for (int i = 0; i < Name.Length; i++)
Serial += string.Format("{0:X2}", (int)Name[i]);
Console.WriteLine(Serial);
效果:
-
050-daxxor
算法難度:⭐⭐⭐
爆破難度:⭐⭐
資訊收集
運作情況:
查殼與脫殼:
無殼:
調試分析
IDA打開程式分析,是個C++程式
搜尋字元串發現提示資訊:You solve it
然後根據提示資訊定位到反彙編,F5一下偷個懶:
這裡就是根據Name生成一個字元串,然後和Serial進行對比,隻要按照生成順序生成一個字元串,即是Serial
注冊機
注冊碼生成算法:
#include
std::cin >> name;
for (int i = 0; i < name.length(); i++) name[i] -= 4;
name.insert(3, "-");
name.insert(5, "-");
name.insert(6, "axd");
std::cout << name;