国赛 strange_int
参考文章地址https://www.52pojie.cn/thread-936377-1-1.html
https://qrzbing.cn/2019/04/27/CISCN2019-strange-int/
下载拿到文件使用linux file命令查看一下文件类型
为DOS/MBR主引导扇区。
放入winhex中查看,发现偏移0x1FE处有55 AA 表示这是一个分区,且主引导扇区的大小位512字节,可以判断前面为主引导记录的代码。
将程序放入IDA中,使用ALT+S修改0-0x200为16位分析的断,并右击code生成他的汇编
MBR:0000 jmp far ptr 7C0h:5
MBR:0005 ; ---------------------------------------------------------------------------
MBR:0005 mov ax, cs
MBR:0007 mov ds, ax
MBR:0009 mov ss, ax
MBR:000B mov sp, 400h
MBR:000E cld
MBR:000F mov ax, 3
MBR:0012 int 10h ; - VIDEO - SET VIDEO MODE
MBR:0012 ; AL = mode
MBR:0014 mov dx, 0
MBR:0017 mov cx, 2
MBR:001A mov ax, 1000h
MBR:001D mov es, ax
MBR:001F assume es:nothing
MBR:001F xor bx, bx
MBR:0021 mov ax, 228h
MBR:0024 int 13h ; DISK - READ SECTORS INTO MEMORY
MBR:0024 ; AL = number of sectors to read, CH = track, CL = sector
MBR:0024 ; DH = head, DL = drive, ES:BX -> buffer to fill
MBR:0024 ; Return: CF set on error, AH = status, AL = number of sectors read
MBR:0026 jnb short loc_2A
MBR:0028
MBR:0028 loc_28: ; CODE XREF: MBR:loc_28↓j
MBR:0028 jmp short loc_28
MBR:002A ; ---------------------------------------------------------------------------
MBR:002A
MBR:002A loc_2A: ; CODE XREF: MBR:0026↑j
MBR:002A cli
MBR:002B mov ax, 1000h
MBR:002E mov ds, ax
MBR:0030 assume ds:nothing
MBR:0030 xor ax, ax
MBR:0032 mov es, ax
MBR:0034 assume es:nothing
MBR:0034 mov cx, 2000h
MBR:0037 sub si, si
MBR:0039 sub di, di
MBR:003B rep movsb
MBR:003D mov ax, 7C0h
MBR:0040
MBR:0040 loc_40: ; DATA XREF: MBR:0012↑r
MBR:0040 mov ds, ax
MBR:0042 assume ds:nothing
MBR:0042 lidt fword ptr ds:6Fh
MBR:0047 lgdt fword ptr ds:75h
MBR:004C
MBR:004C loc_4C: ; DATA XREF: MBR:0024↑r
MBR:004C mov ax, 1
MBR:004F lmsw ax
MBR:0052 jmp far ptr 8:0
首先主引导扇区的地址被加载到0x7c00,跳转的目标地址是0x7c05,之后初始化段寄存器和栈指针,之后int 10h 调用bios中断设置显示模式。
之后int13h,使得将软盘(DL=0H) 上的0磁道(DH=0H)0柱面(CH=0H)2扇区(CL=2H)开始的28个扇区(AL=28H)读取(AH=02H)到内存的1000:0000h处(ES:BX=1000:0)。之后实现跳转
将DS:SI(1000:0)这段地址移动到ES:DI(0:0)处,移动了2000字节(cx=2000h)
接着初始化IDT和GDT(lidt,lgdt)之后开启保护模式(Imsw)并且跳转至32位代码段 从0x200开始
使用ALT+S进行重建
main:00000200 mov eax, 10h
main:00000205 mov ds, eax
main:00000207 assume ds:nothing
main:00000207 lss esp, large ds:0B5Ch
main:0000020E call sub_28B
main:00000213 call sub_283
main:00000218 mov eax, 10h ; DATA XREF: sub_28B+27↓r
main:0000021D mov ds, eax
main:0000021F mov es, eax
main:00000221 assume es:nothing
main:00000221 mov fs, eax ; DATA XREF: sub_283↓r
main:00000223 assume fs:nothing
main:00000223 mov gs, eax
main:00000225 assume gs:nothing
main:00000225
main:00000225 loc_225: ; DATA XREF: sub_28B+11↓o
main:00000225 lss esp, large ds:0B5Ch
main:0000022C xor ebx, ebx
main:0000022E
main:0000022E loc_22E: ; CODE XREF: sub_200+5D↓j
main:0000022E nop
main:0000022F cmp ebx, 10h
main:00000232 jge short loc_25F
main:00000234 mov eax, 80000h
main:00000239 lea edx, ds:0D08h[ebx*4]
main:00000240 mov edx, [edx]
main:00000242 mov ax, dx
main:00000245 mov dx, 8E00h
main:00000249 mov ecx, 21h ; '!'
main:0000024E add ecx, ebx
main:00000250 lea esi, ds:128h[ecx*8]
main:00000257 mov [esi], eax
main:00000259 mov [esi+4], edx
main:0000025C inc ebx
main:0000025D jmp short loc_22E
main:0000025F ; ---------------------------------------------------------------------------
main:0000025F
main:0000025F loc_25F: ; CODE XREF: sub_200+32↑j
main:0000025F ; sub_200+66↓j
main:0000025F call sub_268
main:00000264 int 21h ; DOS -
main:00000266 jmp short loc_25F
main:00000266 sub_200 endp
首先进行初始化 sub_28B和sub_283分别对LIDT和LGDT进行初始化。
首先看sub_28b,初始化中断描述符表
main:0000028B mov edx, 0FCh
main:00000290 mov eax, 80000h
main:00000295 mov ax, dx
main:00000298 mov dx, 8E00h
main:0000029C lea edi, loc_225+3 - unk_100//地址为0x128
main:000002A2 mov ecx, 100h
main:000002A7
main:000002A7 loc_2A7: ; CODE XREF: sub_28B+25↓j
main:000002A7 mov [edi], eax
main:000002A9 mov [edi+4], edx
main:000002AC add edi, 8
main:000002AF dec ecx
main:000002B0 jnz short loc_2A7
main:000002B2 lidt large fword ptr ds:11Ch
main:000002B9 retn
main:000002B9 sub_28B endp
循环了256次,使得ds:[128]处地址填充为800fch之后填充8e00h。
然后加载中断描述符寄存器地址为ds:[11c]。
查看寄存器FF 07 28 01 00 00 1F 00
参考文章操作系统之GDT和IDT
代码段选择器(IDT表基地址)为0x0128
之后再看GDT
main:00000283 lgdt large fword ptr ds:122h
main:0000028A retn
main:0000028A sub_283 endp
只有简短的三行 寄存器地址为 ds:[0x122]
1F 00 28 09 00 00 00
基址为0x0928长度为0x1F
两个表初始化完毕之后继续向下看。
分别给ds,es,fs,gs赋值
后面是一个16次循环,分别将从内存中ds:0xD08开始的数据填充到ds:0x128,而ds:0x128是IDT表基地址,执行后中断21h到30h的的入口地址全部改变
中断编号 | 入口地址 |
---|---|
0x21 | 0xb7c |
0x22 | 0x0B8A |
0x23 | 0x0BA1 |
0X24 | 0X0BC1 |
0X25 | 0X0BE1 |
0X26 | 0X0BFC |
0X27 | 0X0C17 |
0X28 | 0X0C32 |
0X29 | 0X0C4F |
0X2A | 0X0C6C |
0X2B | 0X0C84 |
0X2C | 0X0C96 |
0X2D | 0X0CB5 |
0X2E | 0X0CF7 |
0X2F | 0X0CE0 |
0X30 | 0X0CD4 |
继续向下分析,执行sub_268函数
sub_268 proc near ; CODE XREF: sub_200:loc_25F↑p
main:00000268 mov edi, large ds:0B78h
main:0000026E lea edi, ds:0D48h[edi*4]
main:00000275 mov eax, [edi]
main:00000277 mov large ds:65h, al
main:0000027C mov ecx, [edi+4]
main:0000027F mov eax, [edi+8]
main:00000282 retn
main:00000282 sub_268 endp
函数从ds:0x0B78那获取值作为ds:0D48h的偏移,将ds:0D48h处的值分别赋值给 给ds:65h,ecx,eax
然而地址ds:65h地址为汇编
main:00000264 int 21h
所以此处中断是随着 ds:0d48h[edi * 4]的值改变。此处便是虚拟机。而ecx,和eax就为其参数,之后将0B78处的值加3,从而执行0d48[edi*4]处的下一个中断
main:00000EF8 lea ecx, dword_C78 - unk_100
main:00000EFE mov eax, [ecx]
main:00000F00 add eax, 3
main:00000F03 mov [ecx], eax
main:00000F05 iret
于是我们对各个中断逐一分析,由于涉及0x0b64处地址较多将其简化为buf为DWORD型数组,ecx为c,eax为a
将0x0d48简化为code为DWORD型数组
中断编号 | 操作内容 |
---|---|
0x21 | buf[c]=a |
0x22 | buf[c]=buf[a] |
0x23 | buf[c]=code[buf[a]] |
0x24 | code[buf[c]]=buf[a] |
0x25 | buf[c]=buf[c]+buf[a] |
0x26 | buf[c]=buf[c]-buf[a] |
0x27 | buf[c]=buf[c]^buf[a] |
0x28 | buf[c]=buf[c]<<(buf[a]&0xFF) |
0x29 | buf[c]=buf[c]>>(buf[a]&0xFF) |
0x2A | buf[c]=buf[c]&buf[a] |
0x2B | ds:0x0b78=buf[c] |
0X2C | if {buf[a]==0} ds:0x0b78=buf[c] |
0X2D | if{buf[a]!=0} ds:0x0b78=buf[c] |
0X2E | 暂停 |
0X2F | 打印正确 |
0x30 | 打印错误 |
分析完毕之后,用Lazy IDA将数据dump下来写脚本将其流程打印出来 进行虚拟指令的处理
data = [
0x00000021, 0x00000000, 0x00000081, 0x00000027, 0x00000001, 0x00000001, 0x00000024, 0x00000001, 0x00000001,
0x00000023, 0x00000002, 0x00000000, 0x00000022, 0x00000003, 0x00000002, 0x00000021, 0x00000004, 0x00000008,
0x00000028, 0x00000003, 0x00000004, 0x00000027, 0x00000002, 0x00000003, 0x00000028, 0x00000003, 0x00000004,
0x00000027, 0x00000002, 0x00000003, 0x00000028, 0x00000003, 0x00000004, 0x00000027, 0x00000002, 0x00000003,
0x00000027, 0x00000003, 0x00000003, 0x00000023, 0x00000004, 0x00000003, 0x00000024, 0x00000003, 0x00000002,
0x00000027, 0x00000002, 0x00000004, 0x00000024, 0x00000000, 0x00000002, 0x00000021, 0x00000001, 0x00000001,
0x00000025, 0x00000000, 0x00000001, 0x00000022, 0x00000001, 0x00000000, 0x00000021, 0x00000002, 0x00000081,
0x00000026, 0x00000001, 0x00000002, 0x00000021, 0x00000002, 0x00000009, 0x00000026, 0x00000001, 0x00000002,
0x00000021, 0x00000002, 0x00000009, 0x0000002D, 0x00000002, 0x00000001, 0x00000021, 0x00000000, 0x00000081,
0x00000022, 0x00000001, 0x00000000, 0x00000021, 0x00000002, 0x00000009, 0x00000025, 0x00000001, 0x00000002,
0x00000023, 0x00000003, 0x00000000, 0x00000023, 0x00000004, 0x00000001, 0x00000026, 0x00000003, 0x00000004,
0x00000021, 0x00000004, 0x0000007E, 0x0000002D, 0x00000004, 0x00000003, 0x00000021, 0x00000003, 0x00000001,
0x00000025, 0x00000000, 0x00000003, 0x00000025, 0x00000001, 0x00000003, 0x00000026, 0x00000002, 0x00000003,
0x00000021, 0x00000004, 0x0000005A, 0x0000002D, 0x00000004, 0x00000002, 0x0000002F, 0x00000000, 0x00000000,
0x00000030, 0x00000000, 0x00000000
]
i = 0
while (i<=126):
if data[i] == 0x21:
print ("buf[%d]=%d" % (data[i+1], data[i+2]))
if data[i] == 0x22:
print ("buf[%d]=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x23:
print ("buf[%d]=code[buf[%d]]" % (data[i+1], data[i+2]))
if data[i] == 0x24:
print ("code[buf[%d]]=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x25:
print ("buf[%d]+=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x26:
print ("buf[%d]-=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x27:
print ("buf[%d]^=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x28:
print ("buf[%d]<<=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x29:
print ("buf[%d]>>=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x2A:
print ("buf[%d]&=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x2B:
print ("i =buf[%d]" % (data[i+1]))
if data[i] == 0x2c:
print ("if buf[%d]==0 i=buf[%d]" % (data[i+2], data[i+1]))
if data[i] == 0x2d:
print ("if buf[%d]!=0 i=buf[%d]" % (data[i+2], data[i+1]))
if data[i] == 0x2e:
print "pause"
if data[i] == 0x2f:
print "correct"
if data[i] == 0x30:
print "wrong"
i+=3
打印出结果如下
buf[0]=129
buf[1]^=buf[1]
code[buf[1]]=buf[1]
buf[2]=code[buf[0]]
buf[3]=buf[2]
buf[4]=8
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]^=buf[3]
buf[4]=code[buf[3]]
code[buf[3]]=buf[2]
buf[2]^=buf[4]
code[buf[0]]=buf[2]
buf[1]=1
buf[0]+=buf[1]
buf[1]=buf[0]
buf[2]=129
buf[1]-=buf[2]
buf[2]=9
buf[1]-=buf[2]
buf[2]=9
if buf[1]!=0 i=buf[2]
buf[0]=129
buf[1]=buf[0]
buf[2]=9
buf[1]+=buf[2]
buf[3]=code[buf[0]]
buf[4]=code[buf[1]]
buf[3]-=buf[4]
buf[4]=126
if buf[3]!=0 i=buf[4]
buf[3]=1
buf[0]+=buf[3]
buf[1]+=buf[3]
buf[2]-=buf[3]
buf[4]=90
if buf[2]!=0 i=buf[4]
correct
wrong
进行整理与分析,首先这串代码一共循环9次执行指令
code[i]=code[i] ^(code[i]<<8) ^ (code[i]<<16) ^(code[i]<<24) ^ code[i-1]
之后再执行9次比较
buf[i]与buf[i+9]进行9次比较。
解密脚本:
a=[0x57635565, 0x06530401, 0x1F494949, 0x5157071F, 0x575F4357, 0x57435E57, 0x4357020A, 0x575E035E,0x0f590000,0x0]
for x in range (0,9):
m4=a[x]&0xff
m3=(a[x]&0xff00)>>8
m2=(a[x]&0xff0000)>>16
m1=(a[x]&0xff000000)>>24
p1=m4
p2=p1^m3
p3=p1^m2^p2
p4=p1^p2^p3^m1
flag=p1+(p2<<8)+(p3<<16)+(p4<<24)
print hex(flag)
a[x+1]=a[x]^a[x+1]
得到结果
0x34363065
0x61613564
0x3761352d
0x31312d32
0x392d3965
0x2d303032
0x39653838
0x30386566
0x66616566
将原文patch之后,放入boches之后运行即可提示成功
国赛 strange_int的更多相关文章
- 湘潭邀请赛+蓝桥国赛总结暨ACM退役总结
湘潭邀请赛已经过去三个星期,蓝桥也在上个星期结束,今天也是时候写一下总结了,这应该也是我的退役总结了~ --------------------------------湘潭邀请赛----------- ...
- 2018年数学建模国赛B题 智能RGV的动态调度策略
第一种情况大致思路: 每秒判断各个CNC的状态,若工作完成或者是出于空闲状态下则向RGV发出一个请求.同时,RGV每秒判断自己的状态(上下料.移动.闲置.清洗等),如果是处于闲置状态,则启用调度算法, ...
- 2018年第九届蓝桥杯国赛总结(JavaB组)
懒更,之前的删了补一个国赛总结 记yzm10的第一次国赛(赛点:首都经贸大学) 第一次就拿到了国一,运气不要太好~(同组lz学长豪取国特orz) 从省赛一路水过来,总算有了点成绩.其实最后一题有些遗憾 ...
- 国赛baby_pwn
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造 0x01 ELF文件的动态链接之延迟绑定 在动态链接下,程序模块之间包含了大量的函数 ...
- 使用pwn_deploy_chroot部署国赛pwn比赛题目
目录 使用pwn_deploy_chroot部署国赛pwn比赛题目 一.前言 二.Docker 三.部署镜像 四.pwn_deploy_chroot 五.check && exp 六. ...
- 第九届蓝桥杯国赛+第二天的第11届acm省赛的总结
第九届蓝桥杯国赛+第二天的第11届acm省赛的总结 25号坐的去北京的火车,10个小时的火车,然后挤了快两个小时的地铁,最终达到了中国矿业大学旁边的订的房间.12个小时很难受,晕车症状有点严重,吃了快 ...
- 2019第十届蓝桥杯省赛及国赛个人总结(java-B组)
省赛: 今年省赛的题目比18年简单的多,基本都是暴力枚举.BFS之类.还记得去年在山师考蓝桥杯,我这种辣鸡连题目都没看懂.本以为蓝桥会变得越来越难,没想到今年就被打脸了.今年省赛后面三个编程大题一个没 ...
- Python小白的数学建模课-A1.国赛赛题类型分析
分析赛题类型,才能有的放矢. 评论区留下邮箱地址,送你国奖论文分析 『Python小白的数学建模课 @ Youcans』 带你从数模小白成为国赛达人. 1. 数模竞赛国赛 A题类型分析 年份 题目 要 ...
- 【备考06组01号】第四届蓝桥杯JAVA组A组国赛题解
1.填算式 (1)题目描述 请看下面的算式: (ABCD - EFGH) * XY = 900 每个字母代表一个0~9的数字,不同字母代表不同数字,首位不能为0. 比如 ...
随机推荐
- Python-11-生成器
一.定义 可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用__iter__方法),所以生成器就是一种迭代器. 二.生成器的两种形式 1. 生成器函数 使用yield代替r ...
- Linux忘记root密码操作方法
此方法为:进入单用户模式,直接修改新密码覆盖掉以前的root密码. 操作步骤: 1.进入单用户模式 2.修改root密码 1.进入单用户方法: 1)启动Linux时,通过按上下键(其他键也可以)让Li ...
- GZOI/GXOI2019
陆陆续续做完了-- 与或和(单调栈) 这是一道一眼题-- 看到位运算,按位考虑贡献.对于每一位,将矩阵中的元素变为"当前元素的这一位是否为\(1\)",那么原矩阵变为\(01\)矩 ...
- Java线程之间通讯(三)
使用wait和notify方法实现了线程间的通讯,都是Object 类的方法,java所有的对象都提供了这两个方法 1.wait和notify必须配合synchronized使用 2.wait方法释放 ...
- ITIL《信息技术基础架构库》
一 概述 1. ITIL 自上世纪70年代开始,个人计算机以及计算机网络开始在欧美发达国家普及.随着时间的推移,信息系统的规模越来越大,人们对信息系统的依赖也越来越强.特别是到了80年代,互联网开始普 ...
- docker 入坑3
查看镜像 docker images [OPTIONS] [REPOSITORY[:TAG]] -a, --all=false -f, --filter=[] --no-trunc=false -q, ...
- Python3 MySQL
首先安装pymysql pip install pymysql 准备数据库:创建一个数据库testdb mysql实例: import pymysql #打开数据库连接,使用数据库所在的IP127. ...
- RedisCluster的rename机制失败报错,解决又是数据倾斜问题
需求说明:spring session中的用户session更新是更新key的名字,所以对于key的操作时需要用newkey 替换oldkey value值只允许存在一个,这里用到rename就很合适 ...
- 编译安装php服务报错问题:configure: error: Cannot find libmysqlclient under /usr.
在编译安装php服务时报错: checking for MSSQL support via FreeTDS... nochecking for MySQL support... yeschecking ...
- MySQL Backup--Xtrabackup备份常见错误
1.DDL操作与Xtrabackup备份冲突 当MySQL使用xrabckup进行备份时,如果执行DDL进行表修改,会导致xrabckup备份失败. 错误类似于: InnoDB: Last flush ...