본문 바로가기

리버싱!

rev- llm 코드분석 시 프롬프트

You are an expert CTF player.
Your task is to understand a function in a decompiled binary for a CTF challenge.

The function consists of two steps:
1. 8-bytes of user input are read into a buffer and treated as a 64-bit little-endian integer.
2. This integer is manipulated in a series of arithmetic operations, to produce an XOR key.
3. The XOR key is used to deobfuscate another part of memory.

For now, work on the first step:
1. Identify the `_read` calls to find where the user input is read into a buffer.
2. There should be 8 bytes read, but the buffer is treated as a single 64-bit little-endian integer.
3. Determine the correct user input (as a 64-bit integer).

You have access to the `run_python` tool, which executes a python notebook cell and returns the output.
Do not attempt to perform any calculations yourself, let the `run_python` tool do that.
Do not perform any kind of brute force search, the validation is quite simple, just figure out how to recover the user input directly.

Each byte of the user input should be constrained to a specific value.
If the partial condition is (x == ) or (x != ), then that byte should be .
You can perform a search if you need to, but check each byte individually on the partial condition.

When you have identified the correct user input, run the `UserInput` tool to submit your answer.
Provide the user input as a little-endian hex integer with the 0x prefix.

[CODE]
{code}
[/CODE]
human


Now that you have identified the correct user input, you need to determine the XOR key.

The XOR key will be used in the loop after the VirtualProtect call.

Work step by step:
1. Analyze the code to identify how the user input is used to calculate the XOR key.
2. Carefully examine the logic and translate it to equivalent python code 1 to 1.
3. Use the `run_python` tool to run the python code and calculate the XOR key.

If there are any helper functions, be careful to keep track how the function arguments and return values are used.

When you have identified the correct XOR key, run the `XORKey` tool to submit your answer.
Provide the XOR key as a little-endian hex integer with the 0x prefix.
Also return the key_size (number of bytes) as an integer.
Check the & condition inside the unpacking loop to determine how many bytes are used.

If it is helpful, here is the same code represented in MLIL.
Make sure to pay attention to the number and order of arithmetic operations.

[MLIL]
0x1400275ab :  rdx = &_DstBuf
0x1400275c2 :  0x1a703aa78(0, rdx, 1)
0x1400275c4 :  rdx_1 = &_DstBuf_1
0x1400275d4 :  0x1a703aa78(0, rdx_1, 1)
0x1400275d6 :  rdx_2 = &_DstBuf_2
0x1400275e6 :  0x1a703aa78(0, rdx_2, 1)
0x1400275e8 :  rdx_3 = &_DstBuf_3
0x1400275f8 :  0x1a703aa78(0, rdx_3, 1)
0x1400275fa :  rdx_4 = &_DstBuf_4
0x14002760a :  0x1a703aa78(0, rdx_4, 1)
0x14002760c :  rdx_5 = &_DstBuf_5
0x14002761c :  0x1a703aa78(0, rdx_5, 1)
0x14002761e :  rdx_6 = &_DstBuf_6
0x14002762e :  0x1a703aa78(0, rdx_6, 1)
0x140027630 :  rdx_7 = &_DstBuf_7
0x140027640 :  0x1a703aa78(0, rdx_7, 1)
0x140027642 :  rax = _DstBuf_2
0x140027647 :  rcx = _DstBuf_3
0x14002764c :  rdx_8 = _DstBuf_4
0x140027651 :  r8 = _DstBuf_5
0x140027657 :  r9 = _DstBuf_6
0x14002765d :  r10 = _DstBuf_7
0x140027668 :  if (_DstBuf != 8) then 23 @ 0x140027689 else 28 @ 0x14002766f
0x140027689 :  _Buffer_1 = 0x283a
0x140027690 :  var_19_1 = 0
0x140027695 :  rcx_1 = &_Buffer_1
0x14002769a :  rax_2 = 0x1a703aa50(rcx_1)
0x14002769a :  goto 29 @ 0x1400276a5
0x14002766f :  if (_DstBuf_1 != 6) then 23 @ 0x140027689 else 30 @ 0x140027671
0x1400276a5 :  return rax_2
0x140027671 :  rax_1 = rax | rcx
0x140027673 :  rax_1 = rax_1 | rdx_8
0x140027675 :  if (rax_1 != 0) then 23 @ 0x140027689 else 33 @ 0x14002767b
0x14002767b :  if (r8 != 0x8d) then 23 @ 0x140027689 else 34 @ 0x140027681
0x140027681 :  if (r9 != 0x77) then 23 @ 0x140027689 else 35 @ 0x140027687
0x140027687 :  if (r10 == 0xe) then 36 @ 0x1400276a6 else 23 @ 0x140027689
0x1400276a6 :  _Buffer = 0x293a
0x1400276ad :  var_35_1 = 0
0x1400276b2 :  rcx_2 = &_Buffer
0x1400276b7 :  0x1a703aa50(rcx_2)
0x1400276bd :  var_30 = 0
0x1400276c6 :  var_28_1 = 0
0x1400276cf :  rax_3 = _DstBuf:0.q
0x1400276d4 :  rdx_9 = rax_3 + 0x6274eb3f
0x1400276db :  rcx_3 = &var_30
0x1400276e7 :  0x1400273a0(rcx_3, rdx_9)
0x1400276e9 :  r9_1 = &var_34
0x140027703 :  VirtualProtect(0x14003a3be, 0x1b692, PAGE_EXECUTE_READWRITE, r9_1)
0x140027709 :  i = 0
0x14002770e :  goto 50 @ 0x140027718
0x140027718 :  rcx_4 = i.eax
0x14002771b :  rcx_5 = zx.q(rcx_4 & 0xf)
0x14002771e :  rcx_6 = [&var_30 + rcx_5].b
0x140027723 :  [i + 0x14003a3be].b = [i + 0x14003a3be].b ^ rcx_6
0x140027726 :  i = i + 1
0x140027730 :  if (i != 0x1b692) then 50 @ 0x140027718 else 56 @ 0x140027738
0x140027738 :  r8_1 = var_34
0x14002774c :  VirtualProtect(0x14003a3be, 0x1b692, r8_1, nullptr)
0x140027752 :  rax_2 = 0x14003a3be()
0x140027758 :  goto 29 @ 0x1400276a5

[/MLIL]

DEFCON문제 풀이를 보던중 LLM을 통해 리버싱문제를 해결한사람이 있었다.

성능이 안좋은 모델이였지만 잘해결했고 결국 사람간의 의사소통이 중요한것처럼 LLM과 사람간의 대화인 프롬프트도 어떻게 하느냐가 중요할것같아서 프롬프트를 백업해둔다. (MLIL은 어셈블리와 고급언어 사이 HEX-RAY라고 생각하면 좋을것같다.)