---
# System prepended metadata

title: picoCTF 2025 reverse engineering Write Up (ALL)
tags: [picoctf, reverse engineering, picoctf2025]

---

# picoCTF 2025 reverse engineering Write Up (ALL)
__by #216 NotTooRomantic - young922__


ok, 反正我沒寫過write up,且寫且看

## Flag Hunters

### [題目](https://play.picoctf.org/practice/challenge/472?category=3&originalEvent=74)

反正就是一個很簡單的reverse/pwn? 看看code

```python=
import re
import time


# Read in flag from file
flag = open('flag.txt', 'r').read()

secret_intro = \
'''Pico warriors rising, puzzles laid bare,
Solving each challenge with precision and flair.
With unity and skill, flags we deliver,
The ether’s ours to conquer, '''\
+ flag + '\n'


song_flag_hunters = secret_intro +\
'''

[REFRAIN]
We’re flag hunters in the ether, lighting up the grid,
No puzzle too dark, no challenge too hid.
With every exploit we trigger, every byte we decrypt,
We’re chasing that victory, and we’ll never quit.
CROWD (Singalong here!);
RETURN

[VERSE1]
Command line wizards, we’re starting it right,
Spawning shells in the terminal, hacking all night.
Scripts and searches, grep through the void,
Every keystroke, we're a cypher's envoy.
Brute force the lock or craft that regex,
Flag on the horizon, what challenge is next?

REFRAIN;

Echoes in memory, packets in trace,
Digging through the remnants to uncover with haste.
Hex and headers, carving out clues,
Resurrect the hidden, it's forensics we choose.
Disk dumps and packet dumps, follow the trail,
Buried deep in the noise, but we will prevail.

REFRAIN;

Binary sorcerers, let’s tear it apart,
Disassemble the code to reveal the dark heart.
From opcode to logic, tracing each line,
Emulate and break it, this key will be mine.
Debugging the maze, and I see through the deceit,
Patch it up right, and watch the lock release.

REFRAIN;

Ciphertext tumbling, breaking the spin,
Feistel or AES, we’re destined to win.
Frequency, padding, primes on the run,
Vigenère, RSA, cracking them for fun.
Shift the letters, matrices fall,
Decrypt that flag and hear the ether call.

REFRAIN;

SQL injection, XSS flow,
Map the backend out, let the database show.
Inspecting each cookie, fiddler in the fight,
Capturing requests, push the payload just right.
HTML's secrets, backdoors unlocked,
In the world wide labyrinth, we’re never lost.

REFRAIN;

Stack's overflowing, breaking the chain,
ROP gadget wizardry, ride it to fame.
Heap spray in silence, memory's plight,
Race the condition, crash it just right.
Shellcode ready, smashing the frame,
Control the instruction, flags call my name.

REFRAIN;

END;
'''

MAX_LINES = 100

def reader(song, startLabel):
  lip = 0
  start = 0
  refrain = 0
  refrain_return = 0
  finished = False

  # Get list of lyric lines
  song_lines = song.splitlines()
  
  # Find startLabel, refrain and refrain return
  for i in range(0, len(song_lines)):
    if song_lines[i] == startLabel:
      start = i + 1
    elif song_lines[i] == '[REFRAIN]':
      refrain = i + 1
    elif song_lines[i] == 'RETURN':
      refrain_return = i

  # Print lyrics
  line_count = 0
  lip = start
  while not finished and line_count < MAX_LINES:
    line_count += 1
    for line in song_lines[lip].split(';'):
      if line == '' and song_lines[lip] != '':
        continue
      if line == 'REFRAIN':
        song_lines[refrain_return] = 'RETURN ' + str(lip + 1)
        lip = refrain
      elif re.match(r"CROWD.*", line):
        crowd = input('Crowd: ')
        song_lines[lip] = 'Crowd: ' + crowd
        lip += 1
      elif re.match(r"RETURN [0-9]+", line):
        lip = int(line.split()[1])
      elif line == 'END':
        finished = True
      else:
        print(line, flush=True)
        time.sleep(0.5)
        lip += 1



reader(song_flag_hunters, '[VERSE1]')
```

exploit 在第111、119與121行，如果輸入```";RETURN 0"```話，他會把```"Crowd: ; RETURN 0"```看成兩行，並且因為```"RETURN 0"```符合```re.match(r"RETURN [0-9]+", line)```，因此lip會等於0，也就是會跳到第0段，就是藏有flag的段落。

ans : 輸入"```; Return 0```""

## Quantum Scrambler

### [題目](https://play.picoctf.org/practice/challenge/479?category=3&originalEvent=74)

我只能說這題只要用心看題目都沒問題，看看code

```python=
import sys

def exit():
  sys.exit(0)

def scramble(L):
  A = L
  i = 2
  while (i < len(A)):
    A[i-2] += A.pop(i-1)
    A[i-1].append(A[:i-2])
    i += 1
    
  return L

def get_flag():
  flag = open('flag.txt', 'r').read()
  flag = flag.strip()
  hex_flag = []
  for c in flag:
    hex_flag.append([str(hex(ord(c)))])

  return hex_flag

def main():
  flag = get_flag()
  cypher = scramble(flag)
  print(cypher)

if __name__ == '__main__':
  main()
```
重要的是9~12行，算法是:

假設```A = [[1], [2], [3], [4], [5]```

在i-2的位置把後一格的資料pop掉後加到自己的最尾端

又在i-1的地方把i-2複製到自己的最尾端

:::danger
注意，這裡的i-1實際上是原是資料的i，因為i-1被pop掉了，所以i-1以後全員往前一格
:::

實操一遍就是
```
[[1], [2], [3], [4], [5]] 
[[1, 2], [3], [4], [5]] //第10行
[[1, 2], [3, [1, 2]], [4], [5]]  //第11行
[[1, 2], [3, [1, 2], 4], [5]]  //第10行
[[1, 2], [3, [1, 2], 4], [5, [3, [1, 2], 4]]] //第11行
```

也就是說，其實真正重要的都是每一個index的頭跟尾而已，剩下的都是重複的資料。
還要把是list的元素無視掉(譬如說例子的最後一個index的最後一個元素就是list)。

ans code:

```python=
import pwn

conn = pwn.connect("verbal-sleep.picoctf.net", 57273)

A = eval(conn.recvline())

conn.close()

for i in A:
    print(chr(int(i[0], 16)), end='')
    if(type(i[-1]) is not list):
      print(chr(int(i[-1], 16)), end='')

#不知道為什麼結果後面多一個大括號，把他刪掉，可能演算法有小錯
```

## Chronohack

### [題目](https://play.picoctf.org/practice/challenge/468?category=3&originalEvent=74)

簡單點講，這題超爛，因為你要去猜你跟伺服器得延遲有多久，好評率36%不是蓋的。

這題不多講，因為很爛。

code:
```python=
import random
import time

def get_random(length):
    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    random.seed(int(time.time() * 1000))  # seeding with current time 
    s = ""
    for i in range(length):
        s += random.choice(alphabet)
    return s

def flag():
    with open('/flag.txt', 'r') as picoCTF:
        content = picoCTF.read()
        print(content)


def main():
    print("Welcome to the token generation challenge!")
    print("Can you guess the token?")
    token_length = 20  # the token length
    token = get_random(token_length) 

    try:
        n=0
        while n < 50:
            user_guess = input("\nEnter your guess for the token (or exit):").strip()
            n+=1
            if user_guess == "exit":
                print("Exiting the program...")
                break
            
            if user_guess == token:
                print("Congratulations! You found the correct token.")
                flag()
                break
            else:
                print("Sorry, your token does not match. Try again!")
            if n == 50:
                print("\nYou exhausted your attempts, Bye!")
    except KeyboardInterrupt:
        print("\nKeyboard interrupt detected. Exiting the program...")

if __name__ == "__main__":
    main()
```

第6行可以看到token seed是用時間為種子，所以我們只要把時間猜出來就好了，演算法也不用改，直接套用他的。

爛的地方在於你不知道你跟伺服器的延遲有多，所以你可能試了100遍也試不出來

ans code:
```python=
import pwn
import random
import time

def get_random(length, seed):
    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    random.seed(seed)  # seeding with current time 
    s = ""
    for i in range(length):
        s += random.choice(alphabet)
    return s
def main():
    y = False
    for i in range(0, -50, -50): #這裡根據你的延遲多久調range，網路不好的建議往上調
        if y:
            break
        conn = pwn.connect("verbal-sleep.picoctf.net", 55124)
        conn.recvuntil("(or exit):")
        currentTime = int(time.time()*1000)
        print(f"currentTime: {currentTime}")
        for j in range(0, 50):
            print(currentTime + i + j)
            conn.sendline(get_random(20, currentTime + i + j))
            ret = conn.recvline()
            if ret.startswith(b'S'):
                ret = conn.recvline()
                if ret.startswith(b'Y'):
                    break
                if(j != 49):
                    conn.recvuntil("(or exit):")
                if(j == 49):
                    conn.close()
            else:
                print(ret)
                print(conn.recvline())
                break

if __name__ == '__main__':
    while(1):
        main()
```

附錄一個爆氣real time reaction:

![image](https://hackmd.io/_uploads/H1bel-v2kg.png)

## Tap into Hash
### [題目](https://play.picoctf.org/practice/challenge/466?category=3&originalEvent=74)

這題看起來很難，實則超簡單，真的信我一把

code:
```python=
import time
import base64
import hashlib
import sys
import secrets


class Block:
    def __init__(self, index, previous_hash, timestamp, encoded_transactions, nonce):
        self.index = index
        self.previous_hash = previous_hash
        self.timestamp = timestamp
        self.encoded_transactions = encoded_transactions
        self.nonce = nonce

    def calculate_hash(self):
        block_string = f"{self.index}{self.previous_hash}{self.timestamp}{self.encoded_transactions}{self.nonce}"
        return hashlib.sha256(block_string.encode()).hexdigest()


def proof_of_work(previous_block, encoded_transactions):
    index = previous_block.index + 1
    timestamp = int(time.time())
    nonce = 0

    block = Block(index, previous_block.calculate_hash(),
                  timestamp, encoded_transactions, nonce)

    while not is_valid_proof(block):
        nonce += 1
        block.nonce = nonce

    return block


def is_valid_proof(block):
    guess_hash = block.calculate_hash()
    return guess_hash[:2] == "00"


def decode_transactions(encoded_transactions):
    return base64.b64decode(encoded_transactions).decode('utf-8')


def get_all_blocks(blockchain):
    return blockchain


def blockchain_to_string(blockchain):
    block_strings = [f"{block.calculate_hash()}" for block in blockchain]
    return '-'.join(block_strings)


def encrypt(plaintext, inner_txt, key):
    midpoint = len(plaintext) // 2

    first_part = plaintext[:midpoint]
    second_part = plaintext[midpoint:]
    modified_plaintext = first_part + inner_txt + second_part
    block_size = 16
    plaintext = pad(modified_plaintext, block_size)
    key_hash = hashlib.sha256(key).digest()

    ciphertext = b''

    for i in range(0, len(plaintext), block_size):
        block = plaintext[i:i + block_size]
        cipher_block = xor_bytes(block, key_hash)
        ciphertext += cipher_block

    return ciphertext


def pad(data, block_size):
    padding_length = block_size - len(data) % block_size
    padding = bytes([padding_length] * padding_length)
    return data.encode() + padding


def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))


def generate_random_string(length):
    return secrets.token_hex(length // 2)


random_string = generate_random_string(64)


def main(token):
    key = bytes.fromhex(random_string)

    print("Key:", key)

    genesis_block = Block(0, "0", int(time.time()), "EncodedGenesisBlock", 0)
    blockchain = [genesis_block]

    for i in range(1, 5):
        encoded_transactions = base64.b64encode(
            f"Transaction_{i}".encode()).decode('utf-8')
        new_block = proof_of_work(blockchain[-1], encoded_transactions)
        blockchain.append(new_block)

    all_blocks = get_all_blocks(blockchain)

    blockchain_string = blockchain_to_string(all_blocks)
    encrypted_blockchain = encrypt(blockchain_string, token, key)

    print("Encrypted Blockchain:", encrypted_blockchain)


if __name__ == "__main__":
    text = sys.argv[1]
    main(text)
```

我們可以發現唯一可能放flag的地方是114行的```sys.argv[1]```，
就是執行python時是這樣打指令的 ```python block_chain.py "flag{}"```
所以我們只要專心找哪裡有用到這個text就好了
可以看到有用到的只有encrypt這個函式，所以我們專心看encrypt與他調用的函式

```python=
def encrypt(plaintext, inner_txt, key):
    midpoint = len(plaintext) // 2

    first_part = plaintext[:midpoint]
    second_part = plaintext[midpoint:]
    modified_plaintext = first_part + inner_txt + second_part
    block_size = 16
    plaintext = pad(modified_plaintext, block_size)
    key_hash = hashlib.sha256(key).digest()

    ciphertext = b''

    for i in range(0, len(plaintext), block_size):
        block = plaintext[i:i + block_size]
        cipher_block = xor_bytes(block, key_hash)
        ciphertext += cipher_block

    return ciphertext

def pad(data, block_size):
    padding_length = block_size - len(data) % block_size
    padding = bytes([padding_length] * padding_length)
    return data.encode() + padding

def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))
```
可以發現基本上根本不用管blockchain做了什麼，反正flag是直接崁在modified_text跟plain_text裡的，而plain_text只是為了要讓modified_text有足夠多的字數而填充一些不重要的字符進去而已。
唯一的運算就是拿key跟每個block的資料做xor而已，所以直接把出來的資料xor回去就是原本的字串了。

ans code:
```python= 
import hashlib


def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b)).decode('utf-8')

key= b'\xb1\xcd\xf2\xaekk\x1e\x1fT\x86a*?\xb7\x84p\x97\x89\xdbg.\x92\x87\xa5\xd4\xe3\xba\xc0\xc4\xeb\xd9)'
blockchain = b'\xe5\xe6\xebY+W\xb9\xca\xfd\xb6\x06\x81\x8d8\xf0C\xe3\xb1\xbdZ\x7f\x04\xba\x98\xfe\xe6\x00\x82\x8d;\xf1D\xb2\xb4\xeaX,^\xbd\x98\xf8\xe7\x00\xd6\x80<\xf1\x17\xe3\xb5\xe8\x0b%Q\xec\x98\xfa\xe7\t\x84\x8d9\xfa\x1e\xfb\xe7\xebYxU\xee\x9f\xae\xe5\x03\xd3\x89h\xfbD\xb4\xb2\xeaX/_\xba\x98\xfa\xe2P\xd3\x88h\xacD\xb4\xe1\xeeX(Q\xe5\xcd\xff\xe2\x06\x80\x8f=\xff\x16\xb4\xb5\xbfV-R\xea\xce\xa9\xb3T\xd6\xdai\xfe\x10\xe6\xfa\xeb_/\x06\xee\xc9\xad\xe1U\x81\x8d5\xfbE\xef\xef\xb8\\*V\xe4\xc3\xf9\xb6\x08\x81\xddh\xfaD\xe7\xb2\xab\x06~\x08\x9f\xaf\xda\xaeS\xdb\xd7o\xa3y\xe5\x84\x89\x07K\x0e\x8e\x99\xc8\xe4@\xd4\xe0S\x90s\xbc\x9a\xeb\x1d)^\xbf\xb3\xc3\xa4r\xcd\xd5F\x92\\\x94\x9c\x84\x0cxV\xe9\x9d\xaa\xe7\x05\xca\x81:\xaaC\xe1\xe2\xe2\n/P\xed\x9f\xff\xb1\x07\xd6\x8bi\xfa\x10\xe1\xb5\xef\r$^\xe5\xc2\xf8\xb4\x00\x81\x95<\xf8\x17\xb7\xe4\xecV-T\xb8\xc3\xae\xe1T\x86\xdcn\xfb\x16\xb7\xe5\xb8\x0b\x7f^\xba\xce\xac\xb1T\x85\xdcn\xf0\x1f\xb3\xb3\xbaV.W\xbd\xcb\xaa\xecP\x86\x81;\xfb\x14\xef\xb6\xea\x0e/R\xeb\xcd\xab\xec\x03\xd6\x8c!\xf8\x16\xe2\xb2\xe2_\x7fW\xb8\x9d\xf9\xb1\x02\xd6\x8cn\xa9B\xb5\xee\xeb_|V\xbf\xcc\xf8\xec\x03\x87\x815\xaa\x1f\xb2\xb6\xed^)\x02\xed\xc3\xf8\xe7T\x86\x8fm\xf9\x13\xe5\xb2\xef_|R\xe9\xc3\xf8\xe4\x06\x82\xdc4\xca$'

key_hash = hashlib.sha256(key).digest()

block_size = 16

plaintext = ''

for i in range(0, len(blockchain), block_size):
        block = blockchain[i:i + block_size]
        text = xor_bytes(block, key_hash)
        plaintext += text

print(plaintext)
```

## perplexed

### [題目](https://play.picoctf.org/practice/challenge/458?category=3&originalEvent=74)

這題就是傳統藝能了，比較像正規的reverse

沒code，只有elf，所以我要用截圖的方式。(用ghidra做decompile)

main重要的長醬:
![image](https://hackmd.io/_uploads/rkU7S-whJg.png)

check() reverse後+一些看參數的cout(判斷都沒動):
```cpp=
#include <iostream>
#include <cstring>
#include <bitset>
#define uint unsigned int

int check(std::string input)
{
  size_t sVar1;
  int uVar2;
  size_t sVar3;
  char key [36];
  uint local_34;
  uint local_30;
  int local_2c;
  int j;
  uint i;
  int count;
  int b_count;
  
  sVar1 = input.length();
  if (sVar1 == 0x1b) {
    key[0] = -0x1f;
    key[1] = -0x59;
    key[2] = '\x1e';
    key[3] = -8;
    key[4] = 'u';
    key[5] = '#';
    key[6] = '{';
    key[7] = 'a';
    key[8] = -0x47;
    key[9] = -99;
    key[10] = -4;
    key[0xb] = 'Z';
    key[0xc] = '[';
    key[0xd] = -0x21;
    key[0xe] = 'i';
    key[0xf] = 0xd2;
    key[0x10] = -2;
    key[0x11] = '\x1b';
    key[0x12] = -0x13;
    key[0x13] = -0xc;
    key[0x14] = -0x13;
    key[0x15] = 'g';
    key[0x16] = -0xc;
    b_count = 0;
    count = 0;
    local_2c = 0;
    for (i = 0; i < 0x17; i = i + 1) {
      for (j = 0; j < 8; j = j + 1) {
        if (count == 0) {
          count = 1;
        }
        local_30 = 1 << (7 - (char)j & 0x1f);
        local_34 = 1 << (7 - (char)count & 0x1f);
        std::cout << std::bitset<8>(local_30) << ' ' << std::bitset<8>(local_34) << "\n";
        std::cout << std::bitset<8>(key[i]) << ' ' << std::bitset<8>(input[b_count]) << "\n\n";
        if (0 < (int)((int)input[b_count] & local_34) != 0 < (int)((int)key[(int)i] & local_30)) {
          return 1;
        }
        count = count + 1;
        if (count == 8) {
          count = 0;
          b_count = b_count + 1;
        }
        sVar3 = (size_t)b_count;
        sVar1 = input.length();
        if (sVar3 == sVar1) {
          return 0;
        }
      }
      std::cout << b_count;
      std::cout << '\n';
    }
    uVar2 = 0;
  }
  else {
    uVar2 = 1;
  }
  return uVar2;
}

int main(){
    int a = check("picoCTF{0n3_bi7_4t_a_7im3}a");
    std::cout << a << std::endl;
    return 0;
}
```

直接看上面這個code的輸出:
![image](https://hackmd.io/_uploads/r1JFL-w31e.png)
![image](https://hackmd.io/_uploads/B1pYI-w2ye.png)

可以看出來他就是要核對右邊的char有沒有符合左邊的key，而判斷方式是在key裡每次取7個bit跟右邊我們輸入的char做比對。

知道邏輯後就可以reverse出key每次取7個bit，並且右shift一個bit後就是我們要的輸出。

難的是判斷要從哪裡取7個bit，譬如說:

第一個字是取key[0]的前7個
二是取key[0]的最後一個當第7個bit，而key[1]的前6個當剩下的6個bit
三是key[1]的後2個跟key[2]的前五個
...

以此類推，要去判斷這次是要取誰當第幾個bit是這題偏難的地方，但基本上動動腦子想就能解出來。

ans code:
```cpp=
#include <iostream>
int main(){
    unsigned char key[36] ;
    key[0] = -0x1f;
    key[1] = -0x59;
    key[2] = '\x1e';
    key[3] = -8;
    key[4] = 'u';
    key[5] = '#';
    key[6] = '{';
    key[7] = 'a';
    key[8] = -0x47;
    key[9] = -99;
    key[10] = -4;
    key[0xb] = 'Z';
    key[0xc] = '[';
    key[0xd] = -0x21;
    key[0xe] = 'i';
    key[0xf] = 0xd2;
    key[0x10] = -2;
    key[0x11] = '\x1b';
    key[0x12] = -0x13;
    key[0x13] = -0xc;
    key[0x14] = -0x13;
    key[0x15] = 'g';
    key[0x16] = -0xc;
    int i = 0;
    int c = 0;
    int n = 0;
    char temp = 0;
    while(c!=27){
        if(c % 8 == 0){
            temp = key[c-i] >> 1;
            c += 1;
            i += 1;
            std::cout << temp ;
            continue;
        }else if((c + 1) % 8 == 0){
            temp = key[c-i] & 127;
            c+=1;
            n+=1;
            std::cout << temp;
            continue;
        }
        temp = (key[c-i] << (7 - ((c-n) % 7)))&127;
        temp = temp | ((key[c-i+1] >> ((c-n) % 7) + 1) & 127);
        c+=1;
        std::cout << temp ;
    }
}
```

其實判斷寫的有點爛，有更好的方法請下面留言或跟我講。thx

## Binary Instrumentation 1

### [題目](https://play.picoctf.org/practice/challenge/451?category=3&originalEvent=74)

這應該是我第一次碰見這種題目，這個應該算在reverse裡嗎...
可是題目其實蠻好玩的，解題很好玩，也有其他解法，譬如binwalk下去直接拿string
這邊用正規解，反正我自己是受益良多。

沒code，也不用，等等有要自己寫的code

首先看題目描述:
```I have been learning to use the Windows API to do cool stuff! Can you wake up my program to get the flag?```

可以猜猜應該是用了Sleep()之類的函數(win32裡的函數，不是C stdlib的那個)

再看hint:
![image](https://hackmd.io/_uploads/r1rxiWD3yl.png)
![image](https://hackmd.io/_uploads/Hk2lsbw3kl.png)
好我當時也是看的一頭霧水
但上網找找就有點頭緒了

[頭緒1](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/instrumenting-windows-apis-with-frida)
[頭緒2](https://medium.com/@hexcentric/quickstart-frida-tracing-dll-calls-d820fe795a2d)
//還有一個csdn的其實我蠻想看的，但我驗證碼一直過不了，so i gave up [網址](https://blog.csdn.net/qq_65474192/article/details/138487477)

看起來是一個可以用來呼叫dll的時候改裡面的參數，但我們得先確認是不是Sleep()

cmdline: ```frida-trace -i "Sleep" E:\Downloads\bininst1\bininst1.exe```
output:
![image](https://hackmd.io/_uploads/HkJ9TWw3kx.png)

看來我是猜對了，那直接改裡面的參數就好了
有兩種方法，一種是直接用他自己生成的handler改，就是__handler__/KERNEL32.dll/Sleep.js裡面的參數改掉，或是自己做一個handler出來改，我兩個方法都弄
:::warning
自己寫的話要把cmdline改成```frida-trace -S 檔案名.js E:\Downloads\bininst1\bininst1.exe```
:::

ans code :

直接改\_\_handler__裡面的:
```javascript=
/*
 * Auto-generated by Frida. Please modify to match the signature of Sleep.
 * This stub is currently auto-generated from manpages when available.
 *
 * For full API reference, see: https://frida.re/docs/javascript-api/
 */

defineHandler({
  onEnter(log, args, state) {
    log('Sleep()');
    args[0] = new NativePointer(0);
  },

  onLeave(log, retval, state) {
  }
});
```

![image](https://hackmd.io/_uploads/SyX6RZP3Jg.png)

自己寫:
```javascript=
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'Sleep'), {
    onEnter(args){
        args[0] = new NativePointer(0);
    },
    onLeave(){

    }
})
```
![image](https://hackmd.io/_uploads/ByYFyzw3ke.png)

求出來的答案直接base64 decoder:

![image](https://hackmd.io/_uploads/SyVggzDn1l.png)

## Binary Instrumentation 2
### [題目](https://play.picoctf.org/practice/challenge/452?category=3&originalEvent=74)

基本上跟上一題一模一樣，只是函式變了

題目說明:
![image](https://hackmd.io/_uploads/SJjtlMP3Jg.png)

關鍵字 createfile 跟 write，所以我們去找找跟createfile還有write有關的win32函式
![image](https://hackmd.io/_uploads/rysyWfw2yg.png)
![image](https://hackmd.io/_uploads/HysxZfP2yg.png)

我們來看看是哪個
cmdline : ```frida-trace -i "CreateFile*" -i "WriteFile*" E:\Downloads\bininst2\bininst2.exe```
![image](https://hackmd.io/_uploads/Hy9kMfP3kx.png)

看起來目前只有CreateFileA被找到，那就先看看怎麼改他的參數
![image](https://hackmd.io/_uploads/HyPIMMP2kl.png)
![image](https://hackmd.io/_uploads/BkSwGfwhyx.png)

那就先改改看lpfilename

有鑑於等等後面會用到他的回傳直給WriteFile，我們就直接寫script，不用他自動生成的script。
code:
```javascript=
var buf =  Memory.allocUtf8String("E:/Downloads/bininst2/test.txt");
Interceptor.attach(Module.getExportByName('KERNELBASE.dll', 'CreateFileA'), {
    onEnter(args){
        console.log('CreateFileA() : base');
        args[0] = buf;
    },
    onLeave(retval){
    }
});
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'CreateFileA'), {
    onEnter(args) {
        console.log('CreateFileA() : 32');
        console.log("file path :" + args[0].readUtf8String());
      },
    
      onLeave(retval) {
      }
});
```
這邊KERNELBASE跟KERNEL32分開來看是因為這樣才知道參數有沒有改好，如果沒改好KERNEL32這裡是不會顯示參數的。
又可以看到我有用一個Memory.allocUtf8String()，因為你不能直接改成字串，字串還是char*，所以你要多alloc一個memory去給你要傳入的字串，然後把alloc出來的地址再傳給函數才可以。
![image](https://hackmd.io/_uploads/rkrKNzv21x.png)
![image](https://hackmd.io/_uploads/BkaK4Mw3yg.png)
結果是成功生成了，但打開來是空的，所以我們可以看看有沒有呼叫WriteFile函式了
cmdline: ```frida-trace -S sth.js -i "WriteFile*" E:\Downloads\bininst2\bininst2.exe```
![image](https://hackmd.io/_uploads/S1XeSGDhJe.png)
有呼叫，但是txt裡還是沒有內容，所以我們要去改WriteFile的參數
![image](https://hackmd.io/_uploads/ry-urzw3kx.png)
看到我們要用到檔案的HANDLE，而剛剛的CreateFileA的回傳值就是該檔案的handle，所以直接打code
我們可以先看看他有沒有給第2跟第三參數:
code:
```javascript=
var file ;
var buf =  Memory.allocUtf8String("E:/Downloads/bininst2/test.txt");
Interceptor.attach(Module.getExportByName('KERNELBASE.dll', 'CreateFileA'), {
    onEnter(args){
        console.log('CreateFileA() : base');
        args[0] = buf;
    },
    onLeave(retval){
        file = new NativePointer(retval);
    }
});
Interceptor.attach(Module.getExportByName('KERNELBASE.dll', 'WriteFile'), {
    onEnter(args){
        console.log('WriteFile() : base');
        args[0] = file;
    },
    onLeave(retval){
    }
});
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'CreateFileA'), {
    onEnter(args) {
        console.log('CreateFileA() : 32');
        console.log(args[0].readUtf8String());
      },
    
      onLeave(retval) {
      }
});
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'WriteFile'), {
    onEnter(args) {
        console.log('WriteFile() : 32');
        console.log(args[0]);
        console.log(args[1]);
        console.log(args[2]);
    },
    
    onLeave(log, retval, state) {
    }
});
```
![image](https://hackmd.io/_uploads/BJgP8Mv2kg.png)
答案是2有給3沒有，而2是要寫進去的東西，三是大小，大小隨便定都可以，不要太小就好。

ans code:
```javascript=
var file ;
var size =  new NativePointer(0x00000100);
var buf =  Memory.allocUtf8String("E:/Downloads/bininst2/test.txt");
Interceptor.attach(Module.getExportByName('KERNELBASE.dll', 'CreateFileA'), {
    onEnter(args){
        console.log('CreateFileA() : base');
        args[0] = buf;
    },
    onLeave(retval){
        file = new NativePointer(retval);
    }
});
Interceptor.attach(Module.getExportByName('KERNELBASE.dll', 'WriteFile'), {
    onEnter(args){
        console.log('WriteFile() : base');
        args[0] = file;
        args[2] = size;
    },
    onLeave(retval){
    }
});
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'CreateFileA'), {
    onEnter(args) {
        console.log('CreateFileA() : 32');
        console.log(args[0].readUtf8String());
      },
    
      onLeave(retval) {
      }
});
Interceptor.attach(Module.getExportByName('KERNEL32.dll', 'WriteFile'), {
    onEnter(args) {
        console.log('WriteFile() : 32');
        console.log(args[0]);
        console.log(args[1]);
        console.log(args[2]);
    },
    
    onLeave(log, retval, state) {
    }
});
```
![image](https://hackmd.io/_uploads/rJsGwGPhJx.png)
![image](https://hackmd.io/_uploads/Sy8XwGD3ke.png)
![image](https://hackmd.io/_uploads/Sk5EDzD3Jl.png)

## 心得

不難，花兩天解掉，很多時間都在研究frida跟他的api，學到新東西了
還不錯的體驗，但感覺結口哈很多爽題，但我的段考好像要爆了