> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# crackmes.one - Very Easy Crackme writeup (zh-TW)
- URL: https://taiwanding.com/crackmes-one-very-easy-crackme-writeup-zh-tw/
- Published: 2025-09-22T07:40:36.000Z
- Updated: 2026-07-15T02:49:51.000Z
- Author: Kevin Chen
- Tags: reverse

題目連結：[https://crackmes.one/crackme/67649fee60fa67152406bcac](https://crackmes.one/crackme/67649fee60fa67152406bcac?ref=taiwanding.com)

## 1) 先看 main：定位比對邏輯

在 `FUN_00401000`（等同 main）看到：

- 讀入使用者輸入：`gets_s(local_1f8, 500);`
- 把輸入丟進計算函式：`iVar1 = FUN_00401090();`
- 與常數比較：`if (iVar1 == 0x54A337) ...`

其中 `0x54A337` 十進位是 **5,546,807**。  
**目標**：找任意字串 `s`，使 `FUN_00401090(s) == 5,546,807`，就會顯示 `Good cracker !`。

## 2) 追 `FUN_00401090`：規則

精簡後等價邏輯：

```c
int H(const unsigned char *s) {
    int v = 0;
    for (size_t i = 0; i < strlen((char*)s); i++) {
        v = (v * 64 + s[i]) % 10000000;  // 0x40 = 64
    }
    return v;
}

```

### 「以 64 為基底」是什麼意思？

把整串字元當成一個特製的「進位數」：  
十進位是「每一位 ×10 再加下一位」，十六進位是「每一位 ×16 再加下一位」，  
**這裡是「每一步 ×64 再加上下一個字元的 ASCII」**。  
為了避免爆大，**每一步**都再取餘數 `% 10,000,000`，讓值維持在 0～9,999,999。

### 簡單來說:

就是**一路乘、一路加**的算法：  
從 0 開始，對每個字元重複「`目前值 * 64 + 字元ASCII`，再 `% 10,000,000`」。

## 3) 實例：為什麼 `:Rew` 能過？

字元 ASCII：`':'=58, 'R'=82, 'e'=101, 'w'=119`，逐步：

1. `v=0 → 0*64+58 = 58`
2. `v=58 → 58*64+82 = 3794`
3. `v=3794 → 3794*64+101 = 242,917`
4. `v=242,917 → 242,917*64+119 = ... ≡ **5,546,807** (mod 10,000,000)`

與目標相等 → 輸入 `**:Rew**` 會顯示 `Good cracker !`。

## 4) 為什麼解不只一個？怎麼「拼出」任意解？

1. **先決定最後一個字元該長什麼樣**  
想像驗證公式的最後一步像在做：「前面那堆數 ×64，再加上最後一個字元」。  
既然最後一步會「先乘 64 再加最後一個字元」，那**答案的最後一個字元**，一定要讓整體的「尾數（除以 64 的餘數）」對得上目標值的尾數。
  - 算一下：`r = 目標 T % 64`。本題 `T=5,546,807`，所以 `r=55`。
  - 一個位元組可以 +64、+128、+192 這樣跳，同樣會有相同的尾數。  
  所以最後一個字元 `y` 只可能是：`{55, 55+64, 55+128, 55+192}` 交集 `[0..255]`。  
  → 本題可選 `{55('7'), 119('w'), 183(0xB7), 247(0xF7)}`。  
  直覺記憶：**最後一個字元要把「%64 的尾數」對齊目標。**
2. **把最後一步「倒退一步」**  
一旦選了 `y`，就可以把最後一步「拆掉」：
  - 先算 `T - y`，因為剛好是「前面那堆 ×64」。
  - 再除以 64：`T1 = (T - y) / 64`（這裡一定整除，因為我們挑 y 就是為了對齊）。  
  這樣，原本「整串字」的問題，就縮小成「只要讓**前綴**做到 `前綴的結果 ≡ T1 (mod 156,250)`」就好，因為 `10,000,000 / 64 = 156,250`。
3. **在比較小的世界裡找前綴**  
現在只要找一段短短的前綴 `P`，讓  
`H(P) % 156,250 == T1`。  
用可列印 ASCII（空白到波浪，32–126），長度 2～4 個字元小小搜尋一下，**很快就找到**。
4. **把前綴 + 最後一個字元串起來，就是答案**
  - 選 `y='w'(119)`，會得到 `T1=86,667`，找得到 `P=":Re"` → **`:Rew`**。
  - 選 `y='7'(55)`，會得到 `T1=86,668`，找得到 `P=":Rf"` → **`:Rf7`**（你實測成功的那組）。

### 一句帶走

- **最後一碼對齊 `%64` 的尾數**（決定 y）。
- **把最後一步回推**（算 `T1=(T-y)/64`）。
- **在模 156,250 的小空間找短前綴**，接回 y 就是一組通關密碼。  
因為這樣組得出來的組合非常多，所以答案不是唯一的；但規則固定，所以能過的那些組合每次都一樣會被接受。

## 5) 驗證與自動產生程式

### 驗證某個輸入

```python
def H(s, M=10_000_000):
    v = 0
    for ch in s.encode():
        v = (v * 64 + ch) % M
    return v

print(H(":Rew"))  # 5546807
print(H(":Rf7"))  # 5546807

```

### 自動生成一組「可列印」的解

```python
# 產生一個 H(s) == T 的可列印解（ASCII 32..126）
import itertools

M  = 10_000_000
B  = 64
T  = 5_546_807
PR = list(range(32, 127))  # 可列印 ASCII
MOD_PREFIX = M // B        # 156250

def H_mod(bytes_seq, mod):
    v = 0
    for b in bytes_seq:
        v = (v * B + b) % mod
    return v

def gen_one(max_len_prefix=4):
    r = T % B
    last_candidates = [r + 64*k for k in range(4) if r + 64*k <= 255 and 32 <= r + 64*k <= 126]
    for y in last_candidates:
        T1 = (T - y) // B
        for L in range(1, max_len_prefix + 1):
            for tup in itertools.product(PR, repeat=L):
                if H_mod(tup, MOD_PREFIX) == T1:
                    s = bytes(tup + (y,)).decode('latin1')
                    return s
    return None

print("solution =", gen_one())  # 可能輸出 ':Rew' 或 ':Rf7' 等

```

## 6) 小結

- **主骨幹**：輸入 → `FUN_00401090` → 比對常數 `0x54A337`。
- **規則白話**：每一步 `×64 + 字元ASCII`，每步 `% 10,000,000`。
- **示範解**：`:Rew`、`:Rf7`，還能生成一票答案。

## 經驗分享

關於4)，補充一個更直觀的想法:

1. **每一步都在「繞圈」**  
不是單純一直變大到最後，而是**每一步**都把數字壓回 0～9,999,999（也就是對 10,000,000 取餘數）。
  - `v0 = 0`
  - 讀第1字：`v1 = (v0*64 + ASCII1) % 10,000,000` ＝ `ASCII1`
  - 讀第2字：`v2 = (v1*64 + ASCII2) % 10,000,000`
  - 讀第3字：`v3 = (v2*64 + ASCII3) % 10,000,000`
  - 讀第4字：`v4 = (v3*64 + ASCII4) % 10,000,000`
  - …最後得到的 `vn` 要等於 **5,546,807**。
2. **最後一碼要對齊 `%64`**  
因為最後一步是「前面乘 64 + 最後一碼」，所以**最後一碼的 ASCII** 必須讓  
`最後結果 % 64 == 最後一碼 % 64`。  
也就是「最後一碼 ≡ 5,546,807 (mod 64)」，本題 `5,546,807 % 64 = 55`，  
所以最後一碼可以選 `55('7')`、`119('w')`、`183(0xB7)`、`247(0xF7)` 這類同餘的值。