汇编中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

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
  2. https://github.com/frc123/Win32-Assembly-Learning/wiki/Win32API%E4%B8%AD%E7%9A%84Event

逆向学习-总结报告1

工具

Ollydbg

被调试文件

TraceMe.exe(无壳)

难度

简单

过程

QQ截图20200412161619.png
打开后,发现是需要通过序列号来授权的,取出文本框内的文本需要通过Win32API的GetDlgItemText或GetWindowText
这个是用GetDlgItemText的
ctrl+g然后找到GetDlgItemText的入口位置下int3断点
QQ截图20200412161951.png
点击check后,程序被中断在74944390,F8走出win32api的子程序
QQ截图20200412162208.png
F8一直向下走,在004011f5被条件转移到了0040122e
QQ截图20200412162451.png
发现不太对劲,好像已经被发现我序列号输错了
判断如果不条件转移就破解成功了,所以现在就可以直接把004011f5改成nop就可以不管怎么输入都正确了

序列号算法分析

因此定位序列号检测算法在004011f5条件转移前面,在GetDlgItemText后面
QQ截图20200412162735.png
经过分析后(分析见od注释)发现,004011e5处的call 00401340是唯一一个可能作为验证算法的地方,故判断00401340为序列号验证子程序的入口
004011e5处下断点,ctrl+f2重新开始,运行到004011e5处f7跟进子程序
QQ截图20200412163236.png
QQ截图20200412163243.png
看着汇编指令慢慢分析后,把注释也标在旁边,最后自己翻译成自创的伪代码(怎么舒服怎么写)
QQ截图20200412163525.png
最后只要保证序列号和被这个算法一通算下来的[ebp]一样就行

note

读这段汇编的时候最后稍微卡了一下,因为neg这个指令没仔细了解,也没学过,看名字也就是个正转成负数这类的东西了
但我看到后面sbb的时候意识到问题不对劲,因为如果标志寄存器中的cf位为1的话会导致一个sbb要额外减去1
查了一通发现neg在弄不为0的操作数的时候cf会变成1
所以调用lstrcmpA这个Win32API的时候,目的根本就不是比谁大,而是看是否相同
因为sbb eax,eax不管怎样来说eax-eax都是为0的,但由于cf的存在,它就有可能变成-1或0
而neg操作的对象正是lstrcmpA的返回值(比较对象相同返回0,大于返回正数,小于返回负数)
然后执行完sbb eax,eax后,再来一手inc eax就把结果变成了0和1
所以只要[eax]和[ebp]相同的话,这个序列号检查子程序会返回1(意味着输入正确),不同就0(意味着输入错误)

总结

这个主要还是找下感觉看着书上做的,但发现这个不算难的,做着做着还是发现可以做出来的,分析也不算费劲,前面也说到发现关键子程序做到了唯一定位
后面的序列号检查算法分析(翻译成自己的伪代码)是自己独立做的,后来对比下这个程序的源码发现差不多

参考

段钢-加密与解密
小哈龙-影响标志位的汇编指令-https://blog.csdn.net/qq_22642239/article/details/51442739