闲着没事看了几个讲怎么搞w a i g u a 的视频。。
看完想试试手哈哈哈。
需要用到OD (OllyDbg,windows下搞这种事情的标配?)
目标是一个我很喜欢用的音乐软件,名字就不说了。
纯粹以个人学习为目的。没有恶意。不会传播。
目标程序如果开了一个,再开就会自动切换到已经打开的程序窗口。
根据这个行为,最先想到 给FindWindow EnumWindows 这些下断点。还有什么切换窗口的windows api下断点。 不过对windows api不怎么熟悉,查了几个下下去断点,都没反应。 简直不科学嘛。。看别人破解都是看到目标行为,直接用对应api 下断点就直接找到了。。我这个怎么回事……
OK,既然 这些api断不到, 那我试试退出程序的api。然后往上查试试。
(注意,基本上一直是开一个正常的,然后用OD去开第二个,看行为)
给ExitProcess下了断点。这次的确断到了 ( 废话。。。)。然后查看堆栈,往上倒腾是谁调用了ExitProcess,然后再一路顺藤摸瓜。 (美好的幻想)
发现栈上居然只存了一两层返回位置,然后都看了看,居然没什么有用信息。 (可能是我学艺不精,没分析准栈的内容。也有可能是这是一个新线程?)
(而且后来回想,忘了给查到的位置下断点,看看是谁访问过来的了)
OK,当时没想到这些,既然从 退出往前倒腾 找不到,那我从 程序入口开始往后看,看看能不能找到关键点。
然后从程序入口开始单步执行。 和普通debug也差不多。。不过这个代码是汇编的。
就这样一路跑,用单步执行,不进入,确定主要逻辑在哪个call调用里。然后单步进入这个call里,继续用之前方法,一层一层确认关键位置。
这里的关注点就在切换窗口这里。这是个很明显的动作,一执行我们就能发现。
用上面方法逐渐确定了位置。中间总是出一个毛病,就是给比较深的调用下断点和写注释以后,下次记录就没了,全程序的记录。。
折腾了好几次,开始以为是我的OD出bug了。后来发现 目标程序的行为是 加载某个dll,然后去执行dll里的xxx_main。
对比了一下,似乎一给dll里的位置下断点什么的,下次就会全部丢失。
想了想也正常,dll对应位置本来是没东西的。dll加载了以后才进入到程序的内存地址里。这样重开程序的时候,有些记录在dll的位置里,
这些位置可能被OD认为非法,从而认为这个项目记录dirty了,干脆全部丢掉。
OK,既然如此,我不在OD里记录,我外面开个记事本,记到记事本里。。
试了试还真没毛病了。
而且试了试,DLL加载完的位置每次都是确定的。(这个背后机制暂时不了解。应该有什么规定的,只是我还没看过windows的dll加载机制)
不管怎么样,这个问题解决了。
然后继续找,发现主程序加载dll,然后找一个叫xxx_main的函数,然后执行它。
我们要找的逻辑在这个xxx_main里。
手动在记事本里记下必要信息
逐层继续找。
找了几层以后,发现dll里经常有字符串常量出现(想一想C++里面,直接用一个裸字符串,这个字符串的内存是固定的,每次用的时候都是它的地址)
这些字符串似乎是目标程序debug用的。
不过这里正好便宜了我。。
路上就看见不少 xxx_allow_multi_app_xxx , app_exist这些字符串。
OK,这下心里就有底了,应该已经接近了。
把这层仔细看看。
发现有几个系统调用,CreateMutex,WaitSingleObject,FreeLib什么的。然后再往后还有app_exist,然后碰见了之前找到的会调用ExitProcess的几个位置。
还有大量的其他call,进大概看了看,没发现什么有用信息。还有大量跳转指令。不过都没跳。
CreateMutex后还有个判断。
这个感觉很像了。有可能程序用一个mutex判断有没有已经存在的对象。然后如果怎么怎么样就退出,中间用到FreeLib,然后ExitProcess。
先试试这个CreateMutex是不是用来限制多开的关键。其后有个判断,如果是-1就怎么怎么样,奇怪的是,我们这个第二个程序,通过了检验。
如果修改一下指令,让它强行不通过检验,跳进去的函数是log CreateMutex失败,然后退出。
这个就僵了。应该不是。。因为第二个程序通过检验了。(严格来说应该再去试试 首次开程序的结果)
(后记,去MSDN看了看CreateMutex的惯例用法,就是先Create,如果存在,会返回已存在的那个句柄。然后再用WaitSingleObject这写waitxx 来获取其所有权。所以。。很有可能这个地方的确就是关键点。因为不了解windows api,所以导致没有想到这个。。后面又折腾半天)
OK,既然这里通过了,那关键应该就在下面那个莫名其妙的call,和莫名其妙的跳转里面了。
不过我发现这里call和跳转太多了。。懒得看了。才想起来,这里不是所有跳转都没跳么。那试试首次开程序,对比对比。(早该这样了。。)
OK,发现,两次的行为不一样的地方就在第一个跳转指令。如果首次开程序,这个跳转是执行的。如果第二次,就不执行了。
这个跳转和之前的call应该就是关键点。
看了看,这个跳转判断的是WaitSingleObject执行后eax的值怎么怎么样。
试试,直接强行把 带条件跳转 改成jmp强行跳转。
OK,可以多开。运行也没毛病。应该就只用管这一个地方就行了。
(程序员思路开始作祟了。。万一这个程序还有别的什么资源不能共享,但是没做同步机制什么的,因为觉得开头判断过了。这样程序可能会出状况的。
但是放了放音乐居然没问题,账户也一样。。甚至可以N重放。。。我服了。
想了想,有可能设计初期就是允许多开的,之前不是也看到一个字符串xxx_allow_multi_app_xxx 。
应该是本来做了一些设计允许多开,后来干脆强行把这个设置关了。但是硬跑的话还是没什么大毛病。 (瞎推理一波。。)
)
到这里,如果单纯想多开,差不多了。其实还可以继续挖挖,到底用了什么API查找切换的窗口,为什么之前直接下断点都没断到。
还可以挖挖,之前WaitSingleObject到底在wait什么,用的什么机制从这里得知有没有程序开着。
但是我累了,就TODO吧。(说起来思路很少,关键点也不多,真跑的时候,完全是人脑自己维持一个堆栈。。跑着跑着就忘了我是谁,我从哪来,我要干什么了。。。难度也不大,就是繁琐。。。)
还有最后一部就是把这个关键地址的指令改了以后,写进dll。这样下次就不用再费劲去改了。
OD可以直接保存到文件。很方便。(本来还准备把dll当十六进制码开开,找到指令位置,再手动改。。。)
就这样了。
有空再整排版,深挖之前说的几点。