> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [mp.weixin.qq.com](https://mp.weixin.qq.com/s/FRbGCJJvCjPmjoSPy-JJgA)
上次我们对主函数分析完成了,逆向入门分析实战(一)那么这次我们对子函数 IsAlreadyRun 进行分析。
C 语言代码
IsAlreadyRun 函数的 C 语言代码如下图所示:
下面对其汇编代码进行分析:
rep stosd 之前,同样是入栈操作,我们无需仔细追究,重点关注 call 函数。
首先根据 call ds:CreateMutexA
这一条指令便可得知调用了 CreateMutex 函数(注:CreateMutexA 是在 Ascii 环境下的,CreateMutexW 是 unicode 环境下的)。
上次我们提到过,在函数调用之前,如果有参数需要传递,需要使用 push 先将参数从后往前入栈。那么我们来看下 call ds:CreateMutexA 前面的三条 push 指令分别代表什么意思?如果查呢?这就用到非常常用的一个文档 --msdn 文档,这个文档是微软写的,介绍了 Windows API 函数,基本上遇到的函数都可以在这里查到,比如 CreateMutex 这个函数对应的地址是:
https://docs.microsoft.com/zh-cn/windows/win32/api/synchapi/nf-synchapi-createmutexa
从官网可以看到对应的函数解释,详细介绍了这个函数的作用,参数,返回值等:
三个参数 lpMutexAttributes,bInitialOwner,lpName 中第三个参数 lpName 的意思是互斥对象的名称,另外两个在此处不展开介绍了,感兴趣的可以去查看 msdn 文档。现在我们再回过头来仔细看一下 ida Pro 给的反汇编代码:
是不是和 MSDN 上的三个参数反过来就对应上了?第一条 push 指令对应的是 lpName 这个参数,所以根据 ida pro 右边给出的备注,可以得知这个互斥对象的名称是 TEST。
上一次也提到过,函数调用完成后,VC 中,会使用 eax 寄存器来保存函数的返回值。在这里也是一样的,call ds:CreateMutexA 的返回值会存入 eax 寄存器。
我们继续分析下面的汇编代码:
根据 call __chkesp 可知是调用了栈平衡错误检测函数,这里我们无需理会。之后,
mov \[ebp+var\_4\], eax
cmp \[ebp+var\_4\], 0
jz short loc\_40107D
mov 指令将调用 CreateMutexA 函数的返回值 eax 先移动到 ebp+var_4 的地址上,之后又与 0 对比,如果两者相等,则跳转到 loc_40107D 位置。即,如果 CreateMutexA 函数的返回值为 0,则跳转 loc_40107D 位置。
下面分两种情况进行讨论,一种是 CreateMutexA 函数的返回值为 0,一种是不为 0。
首先分析 CreateMutexA 函数的返回值为 0 的情况:
跳转到这个位置之后,执行 xor eax,eax 这条指令,上次也提到过 xor eax,eax 直接会将 eax 的值设置为 0,这是很常见的一种将 eax 置为 0 的方式。
执行完这条指令后,会到 loc_40107F 位置,根据 pop 出栈指令、call __chkesp、retn 等指令可知,这是子函数 IsAlreadyRun 调用完要返回主函数。
那么,IsAlreadyRun 函数的返回值是多少呢?返回值存在 eax 中,由前面可知,eax 在 CreateMutexA 函数的返回值为 0 情况下,eax 值为 0(xor eax,eax 的作用),所以 IsAlreadyRun 函数的返回值也是 0,即 IsAlreadyRun 返回值为 false。
对应的 C 语言代码:
即,当 hMutex = ::CreateMutex(NULL, FALSE, "TEST") 执行后得到的 hMutex 为 0 时的场景,子函数 IsAlreadyRun 直接执行 return FALSE;
之后,分析 CreateMutexA 函数的返回值不为 0 的情况:
此时,由于 eax 不为 0,所以不会跳转到 loc_40107D 这个位置,而是执行 call ds:GetLastError 执行调用 GetLastError 函数。后面又执行 call __chkesp 调用栈平衡错误检查函数,我们在这里无需理会。之后执行 cmp eax,0B7h 指令,由于 GetLastError 函数的返回值存储在 eax 中,此处其实是在看 GetLastError 返回值是否等于 16 进制的 B7,对应 10 进制的 183。
如果 eax 的值不等于 183,那么跳转到 loc_40107D,之后和上一种情况一样,将 eax 置为 0,然后返回主函数。
如果 eax 的值等于 183,则执行 mov eax,1 指令,然后跳转到 loc_40107F 位置,返回主函数。
那么这个 183 到底代表什么意义呢?通过查询 MSDN 文档,我们可以得知,它刚好对应常数 ERROR_ALREADY_EXISTS。
对比我们写的 C 语言代码:
也就是 GetLastError 函数的返回值等于 ERROR_ALREADY_EXISTS 时,返回 true;否则,返回 false。
到此为止,整个代码分析完毕。感谢各位耐心的阅读,如有不当之处,欢迎指出。
参考书籍:
《Windows 黑客编程技术详解》甘迪文著 -- 北京:人民邮电出版社,2018 年 12 月。
《C++ 反汇编与逆向分析技术揭秘》钱松林,赵海旭著 -- 北京:机械工业出版社,2011 年 9 月。
《恶意代码分析实战》 (美)Michael Sikorski / Andrew Honig 著,诸葛建伟,姜辉,张光凯译 -- 北京:电子工业出版社,2014 年 4 月,原书名:Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software。
《汇编语言》王爽 著 --2 版,北京:清华大学出版社,2008 年 4 月。
实验推荐区
实验:逆向破解 - CrackMe 系列
http://www.hetianlab.com/cour.do?w=1&c=C172.19.104.182016031814360300001
了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!
我就知道你 “在看”