我想坛里的大神们很多参加了上周的BCTF,不知为何exploit全是liunx下的,当时看到只有玩泥巴le,希望有大神指导。玩不来liunx的只有认真的将神秘系统进行了一番分析。
附件里有解密后的idb分析记录,与分析报告。
sm.zip(一)accesscode获取:
神秘的系统,打开发现1FEH 55AA 这是MBR(系统引导)首先分析这部分数据,主要功能就是将数据拷贝到0x8000处然后会利用输入的accesscode 去解密200H~A00H处。
解密算法根据输入的4个(0~9)循环依次解密
第一个肯定是一个call或者jmp,根据opcode可以推导
后面就是一个E8跳偏移,头执行哪个E8 *** BCTF,然后就可以推断出1337
然后解密:
include "stdio.h"
#include <windows.h>
#define SIZE_CODE 0x800
int main()
{
BOOL BRet = FALSE;
DWORD dwsize = 0;
char buf[SIZE_CODE] = {0};
HANDLE hFile = NULL;
hFile = CreateFile("bctfos.3bbbcae1ea13b566477c99941a5eee63",GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, NULL, "error", MB_OK);
return -1;
}
SetFilePointer(hFile, 0x200, NULL, FILE_BEGIN);
BRet = ReadFile(hFile, buf, 0x800, &dwsize, NULL);
if (!BRet)
{
printf("sdas");
return -1;
}
for (int i = 0; i*4 < 0x800; i++)
{
buf[i*4] = buf[i*4] ^ 0x31;
}
for (i = 0; 1+i*4 < 0x800; i++)
{
buf[1+i*4] = buf[i*4+1] ^ 0x33;
}
for (i = 0; 2+i*4 < 0x800; i++)
{
buf[2+i*4] = buf[2+i*4] ^0x33;
}
for (i = 0; 3+i*4 < 0x800; i++)
{
buf[3+i*4] = buf[3+i*4] ^ 0x37;
}
CloseHandle(hFile);
hFile = CreateFile("1.txt", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "1.txt", "error", MB_OK);
return -1;
}
SetFilePointer(hFile, NULL, NULL, FILE_BEGIN);
BRet = WriteFile(hFile, buf, 0x800, &dwsize, NULL);
if (!BRet)
{
return -1;
}
CloseHandle(hFile);
return 0;
}
将得到的数据进行静态分析
(二)文件存储结构分析
配置boches 调试系统
用boches加载镜像,通过前面计算accesscode:1337输入,会得到一个命令行界面:
测试发现wr命令可执行,并可写入数据,,rd命令没有权限,
经过多次调试跟踪,在0x008a5c下断
结合IDA静态分析.....
首先是解析用户的数据
当按下回车(temp-8-5= 0) 会在偏移8A4处验证输入指令是否有效
跟进call sub_86c
之后会计算输入文件名size,并且size<16H
进入call sub_176发现怎么是调用int 13H完成处理屏幕及显示器,设置光标,应该是int 10H,后面跟踪发现调用int 10H完成写扇区,说明int 13h 与 int 10h功能进行了交换:
Int 10 与int 13的交换肯定是在启动系统输入accesscode之前就完成了,因为右面屏幕打印,光标定位都是通过int 13H中断完成的,于是在刚进入系统0a处下断,进入10地址函数
底层知识太差,这应该就是int 10 与 13做交换吧,,,,,紧接着对int 10H的调用
乍一看这不应该是int13H 调用模式么,
先看下es:bx 地址:0xD00
执行完int 10H后
这里完成了读操作,也就确定了上面的交换是将int 10与int 13的ISR做了交换
6A6处的函数等待输入,输入按键是esc 表示输入结束
接下来分析wr的整个过程
(1)调用0x68d处的函数生成一个随机的key序列(2个字节):
(2)读取用户输入的文件内容,同时用生成的key进行加密,加密算法为:
Messge[i] = Message[i] ^ key[i%2] ^ i
此次输入:1 2 3
计算后:b5 ed b5
(3)调用0x6d4处的函数生成一个文件,生成规则是:
1)随机生成一个磁头,磁道,扇区,扇区内偏移值
2)将文件名用0xcc异或加密
3)将以上信息,连同之前的生成的key和文件大小,保存在磁盘的1磁头,0磁道,1扇区处
使用x /20x 0xa000查看写入的文件头
上图中:int flag=01(表示占用),int filesize = 03 int key = de84 filename = bfb5af
经过分析可得知这是文件头存储的结构:
Int flag
Int filesize
Int key
Byte head
Byte cylinder
Byte sector_offset
Byte sector
Chat filename[22]
保存在磁盘的1磁头,0磁道,1扇区处(这里说明了,此系统存储文件时,将头部信息在1磁头,0磁道,1扇区);
(4)调用0x6f1处的函数将内容写入文件,写入的流程是:
1)定位到创建文件时随机生成的由磁头,磁道,扇区,扇区内偏移值组成的位置
将数据随机生成的磁头,磁道,扇区,扇区内偏移值组成的位置,写入es:bx(0xB000)
2)将26字节的文件内容保存在此处 (因为此次调试时输入文件为数据123只有三个)
将加密后的文件写入到 经sector_offset定位的地址+6偏移处
进入call sub_92
3)生成下一个随机的磁头,磁道,扇区,扇区内偏移并保存在此处
4)将下一段26字节的文件内容保存在步骤3)生成的位置,不断循环,直到所有内容都保存在磁盘上。
5)将最后一个文件块的磁头,磁道,扇区,扇区内偏移的值均设置为0xff,表示文件结束。
由于测试时只输入 123
根据 加密算法: Message[i] = Message[i]^key[i%2]^i
密文:b5 ed b5
观察此时,在wr命令后在扇区上(0xa000起始地址)写入的信息
继续跟踪到偏移6d4处的call
功能:
1.定位到创建文件时随机生成的由磁头,磁道,扇区,扇区内偏移值组成的位置
2.将26字节的文件内容保存在此处
3生成下一个随机的磁头,磁道,扇区,扇区内偏移并保存在此处
4.将下一段26字节的文件内容保存在步骤3)生成的位置,不断循环,直到所有内容都保存在磁盘上。
5.将最后一个文件块的磁头,磁道,扇区,扇区内偏移的值均设置为0xff,表示文件结束。
保存加密的文本:
文件块的存储的结构为:
Int flag
Byte head
Byte cylinder
Byte sector_offset
Byte sector
Char message[26]
知道文件的创建和存储方式后,就可以还原系统中的文件了,还原步骤:
(1) 从1磁头0磁道1扇区找到文件头的信息。
也就要将CHS(柱面号,磁头号,扇区号)转化成LBA(线性地址)
计算公式如下:
LBA = ( C – CS ) * PH * PS + ( H – HS ) * PS + ( S – SS )
可是不知道PS(没磁道有多少个扇区),PH(表示每柱面多少磁道)
想想系统在随机创建的磁头,柱面,扇区,扇区内偏移时,随机的值如果超过了最大值 写入磁盘的时候就会出错 所以我生成的随机值 必须保证其在一定的范围之内
在仔细去看看创建的磁头,柱面,扇区,扇区内偏移处的代码吧
上图push 4000001 相当于push 40h;push 0001h,进入call sub_1b2
还要注意扇区是从1开始计数的 磁头是从0开始计数的
上图可以明确 PS(扇区数)=3F
建立随机磁头时,push 20000h 同理可以明确 PH(最大磁头数)=02H
在计算随机扇内偏移时 push 100000H 所以将每个sector分割成10H个,者每个块大小为20H
C/H/S=0/1/1(1磁头0磁道1扇区) = 3F 3F*200 = 7E00
文件名异或0xcc后,发现文件名为key
(2) 得到加密的key=5252H,以及第一个文件块存储的磁头=1,磁道=1,扇区=3A,扇区内偏移=05,定位到第一块文件,取出文件内容
根据第一个文件块处所保存的下一块文件的磁头,磁道,扇区,扇区内偏移,找到
(3) 下一块文件,以此类推,直到下一块文件的磁头,磁道,扇区,扇区内偏移均为0xff
根据:[( C – CS ) * PH * PS + ( H – HS ) * PS + ( S – SS )] *200H +扇内偏移*20 = 1ECA0
上图可知 磁头=0,柱面=05,扇内偏移=06 扇区=11计算的下一存储地址:50CC0
上图可知 磁头=01,柱面=09,扇内偏移=07 扇区=27计算的下一存储地址:9A6E0
上图可知 磁头=0,柱面=0D,扇内偏移=08 扇区=3D计算的下一存储地址:D4500
上图可知 磁头=01,柱面=02,扇内偏移=09 扇区=14计算的下一存储地址:29D20
上图可知 磁头=01,柱面=09,扇内偏移=0b 扇区=30计算的下一存储地址:9B960
(4) 将文件内容拼接起来,用(2)中得到的key解密,即可得到flag:
#include "stdio.h"
#include <windows.h>
#define SIZE_CODE 256
int main()
{
char buf[SIZE_CODE] = {0};
DWORD dwsize = 0;
HANDLE hFile = CreateFile("Decode_.txt", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
char key[] = "\x52\x52";
if (hFile == INVALID_HANDLE_VALUE)
{
return -1;
}
BOOL bRet = ReadFile(hFile, buf, SIZE_CODE, &dwsize, NULL);
for (int i = 0; i < dwsize; i++)
{
buf[i] = buf[i]^key[i%2]^i;
}
printf("buf: %s\n", buf);
CloseHandle(hFile);
system("pause");
return 0;
}
注:本帖由看雪论坛志愿者PEstone 重新将DOC整理排版,若和原文有出入,以原作者附件为准
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课