逆向学习XX客户端如何只运行一个实例

个人认为学习分两种,

  • 一种是当面请教和直接从书本网络中的资料学习.
  • 其二就是看着令你惊叹的作品-顿悟.
  • 什么?顿悟不了?那我们就一起来逆向学习吧!差点忘了,我并不打算提供Demo,这并不重要,难道你打算遇到一个相同的情景?重在方法.

注意:

本文为了照顾新手人群,对于某些内容可能会非常详细的推演.

名词解释:

  1. 一个实例:一个对象,这里指一个程序被创建后在内存中的数量.大白话就是:成功运行了几次.

事由:

今天闲来无事(忙里偷闲)运行了两次Dr_COM的宽带认证客户端.如咱所料,

如图:

我们知道,限制程序只运行一个实例的方法很多,如下主流的几种:

  1. 查找窗口.
  2. 使用互斥对象之类的.病毒经常也这么干.
  3. 使用共享区块.
  4. 使用临时文件.

那我们的COM先生是否如何实现的呢?

开弓or工

好的操起家伙(OllyDBG,IDA)开始一次内科手术.

如果你不是很熟悉这两个工具可以在需要的时候参考这里:

反病毒工具-IDA使用介绍

反病毒工具-OllyDBG使用简介

首先,用OD载入我们Dr.COM

这里的入口点似乎是早期的VS版本编译的Release版本C/C++程序.用工具ExeInfoPE验证一下我们的经验并简单看下是否是伪装壳:

ExeInfoPe的使用方法及介绍可以看这里:

ExeInfo在反病毒领域的用法

我们需要找到哪句提示语“程序xxx已经在运行”,在系统中的位置.在界面中央(反汇编窗口)右键,选择“中文字符串搜索->智能搜索”(这是一个插件,你可以从主流的OD版本中相同位置找到类似的字符串查找功能).弹窗如图:

很幸运,我们找到了它.我们并不是每次都这么幸运,因为一些程序员会将它们加密存储.对于处理种种加密情况我们有机会再谈.

双击这个字符串即可来到这个字符串的地址.

我们看到一个红色箭头指向这里.而本字符串地址的上面是一个jmp,它指向了下面的某处,这表明指令执行的时候只能从远处跳到本字符串地址而不可能是一行一行走下来的.

请牢记我们的目的,我们的目的是找到程序如何进行”是否运行了多个实例”的判断.这个判断导致指令走向两条或者更多的路.其中一条就是这条提示信息.顺藤摸瓜即可.

我们逆着跳到本字符串地址的箭头找到根源.

如上图,我们找到了jmp的根源它就在我们圈住的地方.这是一个动态的jmp.它根据eax的值进行运算,eax*4+0x401D98,运算结果是一个地址跳转表,指向结果.eax就是关键.是谁修改了eax呢?

这里有条mov 指令,原来eax的值来自于[esp+0x174],而esp是栈顶寄存器,只有在运行到该条指令的时候才会有真正参与运算的值.好的.F2在该条指令下断点.然后运行起我们的程序,聪明的OD已经帮助我们计算出了地址,原来eax的值来自于ss:[0018FC64].ss是段选择寄存器,就是我们右下角的窗口显示的内容咯.

我们要监视这个地址是谁将’1’这个值写入其中的.这里提供三种方法:

  1. 紧盯住该地址的内容,我们通过栈回溯跟踪到修改者.
  2. 通过硬件断点,在左下角窗口-“数据窗口”找到该地址,右键”断点”->”写入断点”.长度根据具体情况,这里WORD就行.
  3. 使用RUN跟踪.菜单栏”调试”->”设置条件”,将中断条件设置为该地址为’1’则中断.至于关于强大的RUN跟踪的使用方法,如果你感兴趣,你需要找OD的资料学习.

下图描述了观察栈回溯的两种方法:

皇天不负有心人,我最终找到了将’1’写入的操作,它的地址是:403992.如图:

检测到多个实例的情况已经找到.那么单个示例运行的情况如何呢?我们关闭上面打开的两个Dr.COM程序.现在我们要实验一下只运行一个该程序的时候eax是多少?

将程序拖入OD中一个客户端,找到我们上次的eax*4+0x401D98的地方.发现正常情况下eax的值为A.继续用上面的方法寻找是谁修改了这个eax,找到了一个新的地址403466.这真是很醉,这个程序看来是在判断之后进行了不少其他任务才进行push操作的.VS2005…

既然我们有了两次操作的地址,只要找到它们的共同调用者即可.一般通过反复对比OD中的栈回溯可以帮助我们找到它们的祖先函数.但是这次我不打算用这种毫无趣味的方法.

IDA上场!!!!

反病毒工具-IDA使用介绍

IDA作为一款擅长图形化程序流程的超棒静态分析工具非常适合此次任务.将程序拖入IDA.

小经验:注意:不要快捷方式拖进去.OD是会将快捷方式指向的程序进行分析,**而**IDA连快捷方式本身都不放过!

如图:

如果你发现你的界面是汇编界面而不是像上图的流程图,这往往意味你刚刚不小心关闭了流程图.放心,你可以通过View->Opensubviews->Proximity browsers 重新打开它.

在图形界面按”G”,写上push 0xA的地址”403466”如图:

OK,跳过去:

同样的办法按”G”,写上push 0x1的地址.找到push 0x1在流程图的显示.

在IDA中使用你的Ctrl+鼠标滚轮,可以放大or缩小流程图.让你感觉到更直观.

我的天两幅图竟然没有在一幅”大图”里面!!!这意味这两个函数在调用上相隔很远.

我们通过”大图”的顶端.点击函数名称,按住”X”键可以看到交叉引用的界面.这项功能非常实用,它可以将所有对目标函数或变量的调用列出来.我们看到对于push 0xA这一路的顶端函数只有一个父函数调用过它.双击这个父函数.

这下面的图中,我们看到它们的关系:

让我们放大分道扬镳之地.如图:

从后往前看,如果cmp结果是0,就会跳到右边(loc_403A99),没有发现已经运行了本程序.cmp比较的是eax是否等于0FFFFFFFF(八个F).如果等于就是正常.OK,显然是call sub_40A4E0做了某种判断导致eax的值.

现在记住eax如果等于0FFFFFFFF就是没有检测到其它示例.

在OD中进入这个函数:

发现新大陆

我和我的小伙伴们都惊呆了.这完全超出了我的预期.本来以为这个程序会用传统的实例检测方法,但是我们看到了CreateFileMappimg之类的函数.初步推断此程序利用多次映射文件产生的冲突来做的文章.继续看:

我们发现一个新的分道扬镳的跳转,它位于40A529.

它用下面这句做了比较:

cmp ebp,0xB7

向上追溯:

mov ebp,eax

ebp来自eax.按照约定,函数的调用可以修改eax的值来达到传递结果的目的.而这里的eax正是GetLastError函数的执行结果.

地址40A529的代码的意思是如果执行GetLastError返回的不是0xB7,则跳走.跳走的结果是:

0040A54A   mov eax,edi

而此时的edi为FFFFFFFF,回想上面,这个值意味着我们没有检测到另一个示例在运行.

有同学可能会问,’0xB7是嘛意思呀?随机的一个值?’.当然不是,0xB7是错误码,它拥有明确的含义,你可以通过VisualStudio中菜单栏中的工具->”错误查找”小工具,找到它的含义:

果然如此.

收工总结

到此我们已经达到了我们预期的目的-探明Dr. Com检测到自己是否只运行了一个实例的方法.通过逆向分析了解到它是通过:

设置CreateFileMapping函数的最后一个参数MapName,该参数是一个共享值.如果该值被重复命名,会报错,该错误可以通过GetLastError函数得到,返回值为ERROR_ALREADY_EXISTS(0xB7).

知识拓展

更多关于该函数的情况可以阅读这里:

CreateFileMapping用法

学到了什么?

预计一名纯新手读者通过阅读本文会学到:

  1. OD对程序的基本分析方法.
  2. 栈回溯概念
  3. 利用IDA流程图分析程序.
  4. 一种独特的进程互斥方法.(目的)
  5. ……

我从中学到了什么:

  1. 复习了以上所有知识
  2. 沿途看了数个感兴趣的WindowsAPI的实现.
  3. 直接下断常用互斥函数找捷径差点被坑.
  4. 了解了一些字符串函数和目录路径设置函数的用法.
  5. ……

尾声

在我幼稚园时,老师问我,”你从哪里懂得这么多”.”我的老师是书本.”我回答道.

有人说书本是不会说话的老师.如果你学到更多知识,请珍惜和程序在一起的时光吧.

有问题请留言评论以便讨论.

如果本文为你来到了新鲜空气,请点(~ o ~)~

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-08 18:59:01

逆向学习XX客户端如何只运行一个实例的相关文章

只运行一个实例以及内存泄漏检测

unit 使应用程序只运行一个实例; interface uses Windows; const  // - 互斥体唯一的名字  _Mutex_Name = '{19631971-1976-1981-1989-199319941995}'; var  _Mutex_Handle: THandle; implementation initialization // - 载入时调用的代码 // - 创建互斥体对象_Mutex_Handle := CreateMutex(nil, False, LPC

用命名事件对象来实现只运行一个实例

1.添加CWinApp派生类的成员变量 HANDLE _hEvent; 2.在InitInstance中创建命名事件对象,并设置为有信号状态 //创建事件对象,可用命名事件对象来控制只运行一个实例     _hEvent=CreateEvent(NULL,                                              FALSE,                                  //TRUE人工重置,FALSE 自动重置               

vc++高级班之窗口篇[4]---让程序只运行一个实例

大家都看过或者使用过类似只运行一个实例的程序,比如:QQ游戏.部分浏览器 等等! 让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建后,有窗口的程序在窗口创建前, 检查系统中是否已经设置了某些特定标志,是否创建了一些全局唯一的东西,或者让程序的多个实例都能看到的东西, 如果有则说明已经有一个实例在运行了,则当前程序通知用户如何如何,然后程序退出,当然方法有很多种,各有各的优缺点! ①.创建互斥体 Mutex 法: 但是单纯的使用互斥体的话不能取得已经创建的实例窗口局柄,因此无

37.QT-QTSingleApplication-程序只运行一个实例

QTSingleApplication由Qt官方提供的,用于实现只启动一个实例,并在启动时可以向向另一个实例通信(依赖于QtNetwork模块) QTSingleApplication下载路径:链接:https://pan.baidu.com/s/1bBLgkpOW3lq6ZQj9ultxyA 提取码:2n9h 1. 添加源码到工程 将QTSingleApplication解压,将解压后目录拷贝到当前项目路径下,在项目pro文件中增加下面这行 2. 修改main.cpp,将以前QApplicat

C#让应用程序只运行一个实例的几种方法

一 判断是否有相同的实例已经运行 1 根据“Mutex”判断是否有相同的实例在运行 /// <returns>已有实例运行返回true,否则为false</returns>public bool IsRunningProcessByMutex(){     bool createNew;     using (System.Threading.Mutex mutex = new System.Threading.Mutex(true, Application.ProductName

[VC]在VC++中实现让程序只运行一个实例的方法且实现该实例

方法一: 有时候在开发应用程序时,希望控制程序运行唯一的实例.例如,最常用的mp3播放软 件Winamp,由于它需要独占计算机中的音频设备,因此该程序只允许自身运行唯一的一个例程.在Visual C++的开发实践中,对于16位的Windows系统,应用程序的hPrevInstance句柄保存了应用程序上一个运行的实例,可以用该值来检查是否 有实例运行:然而在32位Windows系统下,这个值总是NULL,所以无法利用该值来实现程序运行唯一实例.本实例给出了解决这个问题的简单办法,只 要将程序中稍

使程序只运行一个实例

//APP 里面 //1.// 此程序只能运行一次,用互斥量来判断程序是否已运行 1 HANDLE m_hMutex=CreateMutex(nullptr,TRUE, m_pszAppName); 2 if(GetLastError()==ERROR_ALREADY_EXISTS) { return FALSE; } //2. 1 HWND hWnd = FindWindow(nullptr, m_pszAppName/*_T("MyDlg")*/); //第一个参数: 窗口的类名

让程序只运行一个实例

Windows 下一个典型的特征就是多任务,我们可以同时打开多个窗口进行操作,也可以同时运行程序的多个实例,比如可以打开许多个资源管理器进行文件的移动复制操作.但有时出于某种考虑(比如安全性),我们要做出一些限制,让程序只能够运行一个实例.在Delphi编程中,笔者总结出了以下几种方法: 一. 查找窗口法 这是最为简单的一种方法.在程序运行前用FindWindow函数查找具有相同窗口类名和标题的窗口,如果找到了,就说明已经存在一个实例.在项目源文件的初始化部分添加以下代码: Program On

让程序只运行一个实例(Delphi篇)(三种方法,其中使用全局原子的方法比较有意思)

Windows 下一个典型的特征就是多任务,我们可以同时打开多个窗口进行操作,也可以同时运行程序的多个实例,比如可以打开许多个资源管理器进行文件的移动复制操作.但有时出于某种考虑(比如安全性),我们要做出一些限制,让程序只能够运行一个实例.在Delphi编程中,笔者总结出了以下几种方法: 一. 查找窗口法 这是最为简单的一种方法.在程序运行前用FindWindow函数查找具有相同窗口类名和标题的窗口,如果找到了,就说明已经存在一个实例.在项目源文件的初始化部分添加以下代码: [delphi] v