NewStar re ptrace wp

image-20241025152421093

通过 fork() 函数创建子进程,返回值 v11 区分父进程和子进程:

  • 如果 v11 > 0,则表示当前是父进程,v11 记录子进程的 ID。

    通过 wait(stat_loc) 等待子进程停止或退出。

    使用 ptrace(PTRACE_POKEDATA, addr, addr, 3) 操作向地址 addr 写入数据 3,该操作通常用于调试。

    ptrace(PTRACE_CONT, 0, 0, 0) 继续执行子进程,直到下一个停止事件。

    再次等待子进程的状态变更。

  • 如果 v11 == 0,表示当前是子进程。

    ptrace(PTRACE_TRACEME, 0, 0, 0) 告知内核当前进程允许父进程对它进行调试。

    execl("./son", "son", &s, 0) 执行另一个程序 son,并传递变量 s 的地址作为参数。

son文件:

image-20241025153658925

注意sub函数,可疑,看看,这里应该就是产生Flag的地方

image-20241025153805694

dword是4,但是我们需要注意的是之前的ptrace(PTRACE_POKEDATA, addr, addr, 3)会把4修改成3

addr的地址恰好是这个地址

image-20241025153913838

我们可以写解密脚本

data = [0xCC, 0x8D, 0x2C, 0xEC, 0x6F, 0x88, 0xED, 0xEB, 0x2F, 0xED,
  0xAE, 0xEB, 0x4E, 0xAC, 0x2C, 0x8D, 0x8D, 0x2F, 0xEB, 0x6D,
  0xCD, 0xED, 0xEE, 0xEB, 0x0E, 0x8E, 0x4E, 0x2C, 0x6C, 0xAC,
  0xE7, 0xAF]

for i in range(len(data)):
    # 也可以是data[i] = (data[i] << 3 | data[i] >> 5) % 256
    data[i] = (data[i] << 3 | data[i] >> 5) & 0xff
    print(chr(data[i]),end='')

在这里解释下为什么son的a2是s吧

execl("./son", "son", &s, 0);

execl 的第一个参数 "./son" 指定要执行的程序路径,后面的 "son"&s 作为命令行参数传递给新程序。execl 将这些参数形成一个新的 argv 数组传递给新程序的 main 函数。因此,新进程的 argv 将会包含以下内容:

  • argv[0]:包含 "son",通常表示程序名称(这是execl的约定,程序自身名称作为 argv[0])。
  • argv[1]:包含 &s 的值,传递给新程序的 main 函数。

sub_600011AD 中:

s = *(char **)(a2 + 4);
  • a2argv 的指针,指向 argv[0] 的地址,因此 a2 + 4 指向 argv[1],即传递的 s 地址。

sub_600011AD 中的 a1

int __cdecl sub_600011AD(int a1, int a2) 中,a1 通常是 argc,即参数的数量。一般情况下,a1 会用来表示命令行参数的个数,以便 sub_600011AD 函数可以判断是否接收到正确数量的参数。

a2 作为 argv 的指针,其值表示 argv[0] 的地址。因为每个指针占用 4 个字节(在 32 位系统下),所以 a2 + 4 指向 argv[1],即传递的 s 的地址。这就是为什么 sub_600011AD 使用 a2 + 4 来取得 s 的地址。