2020年4月

2020KCTF看雪攻防赛第一题解析

第一题 十二生肖

人生中的第一道攻防题被我做出来了哈哈,看雪名次第86(我没报名提交不了),但当时只有85个人提交哦
补充:然后后来写完这篇文章我就去报名了,114名了。。。
其实答案早就算出来了,不知道干啥我跑到atoi里看半天(不熟悉C/C++的后果,立个Flag,学好C++)

题目

https://ctf.pediy.com/game-season_fight-135.htm
spring2020.rar

工具

1. IDA

QQ图片20200415110346.jpg

2. OllyDbg

images.jpg

解析

打开IDA,看String,发现good
跳转到使用这个数据的代码段
QQ截图20200415002852.png
看sub_401750的xrefs,我们来到401a3b
QQ截图;20200415003115.png
我们发现

.text:00401A3B                 call    sub_401750

这一行会导致弹出“right”;

text:00401A42                 call    sub_401790

这一行会导致弹出“wrong”
所以我们要让eax=50h
QQ截图20200415003329.png
我们从00401A36开始倒着推分析:要让eax=50h,在

.text:00401A34                 sar     eax, 1   

这一行前面要让eax=A0h

.text:00401A31                 cdq
.text:00401A32                 sub     eax, edx

再看这个cdq,cdq把EAX的第31bit复制到EDX的每一个bit上,换句话说:

如果eax为正数,edx将会为0
如果eax为负数,edx将会为-1

然后分析“sub eax, edx”:

如果eax为正数,eax=eax-edx=eax=0
如果eax为负数,eax=eax-(-1)=eax+1

要让eax=A0h,eax为负数的话怎么弄都不可能让eax变成A0h,因为给1个负整数+1是必然小于等于0的
所以eax一定是正数,且eax=A0h
现在我们开始正着推(好像我分析的时候是先正着推的?我忘了。。。)
QQ截图20200415004126.png
打开OD,在401a1b处下断点,然后运行-输入“123456”-按确认,程序被中断了,然后eax为字符串的“123456”
QQ截图20200415004525.png
然后F8往后走,发现atoi被调用了,且“123456”是参数
atoi是把字符串变成整数的参数
所以在调用完毕后,eax中存放的就是输入的字符串的整数型
QQ截图20200415004525.png
F8走到call atoi后面,发现eax果然就是输入的字符串的整数型
(1E240h=123456)(十六进制1E240=十进制123456)
QQ图片20200415004945.png
回到ida,.text:00401A28开始的那三行仔细看看就知道完全没用(秘制代码),我们正着推的和逆着推的总算碰到一块了

总结/结论/答案

放进去的是A0h的十进制字符串就对了,A0h的十进制字符串就是这道题的答案:160

Win32API中的Event

刚学到这个感觉有点模糊/抽象,所以稍作整理

CreateEvent

创建一个Event返回一个句柄(失败返回0)

参数

  • lpEventAttributes
    指向一个SECURITY_ATTRIBUTES结构(就是句柄可不可以被继承那个东西)
  • bManualReset
    为TRUE的话必须要用ResetEvent手动复位;FALSE的话当测试事件的函数返回时,对象会自动被复位

到这里就很容易懵逼,复位?置位?测试事件?没关系等待再看,看到后面不懂再会过来一起看就行

  • bInitialState
    TRUE则初始状态为置位;FALSE则初始状态是复位

(懵逼2连)

  • lpName
    指针:事件对象名称

WaitForSingleObject

等待Event:等待一个Event置位或超时就返回

参数

  • hHandle
    事件对象句柄
  • dwMilliseconds
    超时时间

Note

换句话说,则个函数的作用就是等待一个Event对象
如果在WaitForSingleObject调用时,事件对象状态为复位的话,线程将会在WaitForSingleObject处等待(不返回),直到事件对象状态变为置位或超时(超时时间:dwMilliseconds)后,WaitForSingleObject才会返回。如果调用CreateEvent时,参数bManualReset指定FALSE的话,WaitForSingleObject返回后会又自动把这个Event的状态给换成复位
现在再看看CreateEvent中的参数bManualReset应该也就不懵逼了。测试事件的函数在这里就是WaitForSingleObject
所以总结下,复位状态就得等,置位了才能过去(这话感觉好直白)

SetEvent

手动将Event状态设为置位

参数

  • hEvent
    事件对象句柄

ResetEvent

手动将Event状态设为复位

参数

  • hEvent
    事件对象句柄

补充说明

  • 置位在官方文档中被称为signaled
  • 复位在官方文档中被称为nonsignaled

本文还在这些地方发布了

  1. https://blog.csdn.net/u013289971/article/details/105502368

汇编中call指令和其对应的机器码

正文

call这个指令很神奇,他和别的指令不太一样

对比下,当我们执行

mov    eax,1

的时候,他的对应机器码是

66 B8 01 00 00 00

这其中“66 B8”对应的是“mov eax,x”
后面的“01 00 00 00”就是“1”在32位中的Little Endian了
所以说,在对mov指令进行汇编时,里面的常数会被直接汇编成机器码

然而call指令比较有意思
这个是一段很简单的汇编代码

Start:
    call    @F
@@:    pop        ebx

    call    @F
    nop
    nop
    nop
@@:    pop        ebx
    
    end    Start

然后通过反汇编后,他变成了这个样子
QQ截图20200413220226.png
仔细观察机器码和反汇编代码的对应关系,在汇编代码中,第一行的

call    @F
@F:    ......

被汇编成了机器码

E8 00 00 00 00

欸?“@F”所在地址明明是00401005,为什么没有被汇编成为

E8 05 10 40 00

呢?
再接着看下一个例子,这是汇编代码

call    @F
nop
nop
nop
@@:    ......

被编译器编译后变成了

E8 03 00 00 00

我好像发现了什么,编译器你好坏哦。。。
原来call编译后会把已call所在位置为基址,然后把被call的位置的偏移地址给汇编成字节码
编译器:你写的什么辣鸡玩意,让我来
我:我用od把你编译的东西全改了(:逃
开个玩笑,这个call的“隐藏”作用其实挺大的(大在哪?下次再写),改掉的话就傻*了~况且谁闲的没事改,偏移地址他不香吗(我在说什么,一个功能在这里水了5行)
现在我们把王爽的汇编语言(第3版)翻到第192页,我们看到执行

call    标号

的时候实际上在执行

push    eip
jmp    标号

(这段被我改成32位的了,书上是16位)
所以拓展下,jmp的原理和call其实是一样的,这里主要研究call
通过call这个特性,我们可以简单的做到重定位(作用就下一篇再写了)

其他发布

  1. https://blog.csdn.net/u013289971/article/details/105508135
  2. https://cqp.cc/t/49257