逆向入口切入

 谨以此菜文献给NE365和FCG。以资新年庆贺!也祝所有圈内人士新年吉祥!

逆向分析,最开始的一步就是寻找入口点。虽然可以破墙而入,但为了防止“墙后面还是一堵墙”,因此我们还是喜欢去撬门锁。一般来说,破解的话,找一些MessageBox注册提示,网络程序会考虑搞网络api,找到了正确的入口,就可以登堂入室,为所欲为。
但是一般的软件逆向分析,入口或许稍微难找一些,有时因为找不到正确的分析入口而放弃分析。本次目标是某股票分析软件。
该软件将根据股市信息计算出的数据以柱状显示在坐标系中。我们的目标是找到计算这些数据的代码。找到了这些代码,要弄懂他们就只是时间问题了。
既然是柱形图显示,希望大家先看一下GDI编程,稍微瞅瞅就行,毕竟这里的关键是找到计算柱形图数据的代码。柱形图变换菜单为“查看”/“显示柱状参数”,因此可以从这个菜单消息入手。
找菜单消息处理有很多方法,我比较常用相近处理法。因为菜单消息处理函数都离得很近,所以如果可以找到一个菜单的处理地点,其他菜单函数也相应不远了,这就是我所谓的相近处理法,因为我不会脱壳,也懒得学OD那些高级技巧,被逼无奈而已……….
先运行股票分析软件,经过研究,F7键对应的“今日提示”菜单,有点MessageBox的味道,于是用OD附加进程。下MessageBox断点,在软件中按F7,被OD拦截,Ctrl + F9返回调用处:

0041A64B   push 0        
0041A64D   push gszt.0043B2FC       ; │Title = "今日提示"
0041A652   push gszt.00491E80       ; │Text = "  …..?...
0041A657   mov ecx,dword ptr ss:[ebp+8]     ; │
0041A65A   push ecx         ; │hOwner
0041A65B   call dword ptr ds:[42D17C]     ; /MessageBoxA
0041A661   jmp gszt.00422EB2

继续Ctrl + F9返回上一层调用:

77E1A411   push dword ptr ss:[ebp+18]
77E1A414   push dword ptr ss:[ebp+14]
77E1A417   push dword ptr ss:[ebp+10]
77E1A41A   push dword ptr ss:[ebp+C]
77E1A41D   call dword ptr ss:[ebp+8]        
77E1A420   cmp dword ptr ss:[esp+4],DCBAABCD
77E1A428   je short user32.77E1A43B

幸运的话这个call dword ptr ss:[ebp+8]或许是菜单消息主处理函数,如果在这里下断点,拦不住其他菜单操作,那可以尝试继续Ctrl + F9返回更上一层调用函数,一直到发现主处理函数。但不幸的是上面代码处于User32.dll领空,更不幸的是对软件的任何操作都会被这个函数拦截,事实上这个函数是消息处理函数,如B哥所说,晃一下鼠标都要被他拦住。
不过正所谓柳暗花明又一村,如果你乐意,可以在这里下条件断点,但我更愿意继续翻一下最开始的MessageBox 。光标定位在

0041A64B   push 0   ; /Style = MB_OK│MB_APPLMODAL

根据OD提示,该句代码被引发自:

0041A513   mov ecx,dword ptr ss:[ebp-7C]
0041A516   xor eax,eax
0041A518   mov al,byte ptr ds:[ecx+42303F]
0041A51E   jmp dword ptr ds:[eax*4+422FDF] ;-------典型的消息Table跳转

上面的jmp,是比较典型的条件跳转,根据eax不同,跳向不同位置。在这个地方下断点,会发现一般的操作,软件不会被中断了,说明大部分消息处理都不经过这个地方。但是随便找一个菜单点一下,很明显被断在这个地方。
那么点一下需要分析的柱形参数菜单,被OD拦截在此,继续单步运行,如下:
0041C47E   movsx edx,word ptr ds:[447102]
0041C485   neg edx
0041C487   sbb edx,edx
0041C489   inc edx
0041C48A   mov word ptr ds:[447102],dx
0041C491   movsx eax,word ptr ds:[447102]
0041C498   test eax,eax
0041C49A   jnz short gszt.0041C4DE   ;------跳

到此:

0041C4DE   push gszt.0043B3A4      
省略几句…….
0041C4EF   mov eax,dword ptr ds:[446C98]
0041C4F4   push eax           ; │hMenu => 02170742
0041C4F5   call dword ptr ds:[42D180]     ; /ModifyMenuA
0041C4FB   push gszt.0043B3B4      
0041C500   push 91           ; │NewItemID = 91 (145.)
0041C505   push 0   ; │Flags = MF_BYCOMMAND│MF_ENABLED│MF_STRING
省略几句…….
0041C510   call dword ptr ds:[42D174]         ; │/GetMenu
0041C516   push eax                     ; │hMenu
0041C517   call dword ptr ds:[42D180]         ; /ModifyMenuA
0041C545   add edx,97
省略几句…….
0041C54B   mov dword ptr ds:[446CDC],edx
0041C551   push 1         ; /Erase = TRUE
0041C553   push gszt.00446CD0     ; │pRect = 00446CD0 {732.,-80.,837.,636.}
0041C558   mov eax,dword ptr ss:[ebp+8]     ; │
0041C55B   push eax           ; │hWnd
0041C55C   call dword ptr ds:[42D188]     ; /InvalidateRect
0041C562   xor eax,eax
0041C564   jmp gszt.00422EB2

清注意右边的注释,上面说白了就是修改一下菜单提示内容,然后调用InvalidateRect 函数。再往下就跳出当前函数:

00422EB2   pop esi                     ; 0012FF20
00422EB3   mov esp,ebp
00422EB5   pop ebp
00422EB6   retn 10

F8单步跟出去:

77E1A41D   call dword ptr ss:[ebp+8]     ;-----刚才所在的函数
77E1A420   cmp dword ptr ss:[esp+4],DCBAABCD ;-----返回处
77E1A428   je short user32.77E1A43B     ;-----跳

到此:

77E1A43B   add esp,8
77E1A43E   pop ebp
77E1A43F   retn 14

继续跟出去,再返回几次,回到主进程领空:

00419B4D   push 0                     ; /MsgFilterMax = 0
00419B4F   push 0                     ; │MsgFilterMin = 0
00419B51   push 0                     ; │hWnd = NULL
00419B53   lea edx,dword ptr ss:[ebp-1C]     ; │
00419B56   push edx                   ; │pMsg
00419B57   call dword ptr ds:[42D1D0]       ; /GetMessageA
00419B5D   test eax,eax
00419B5F   je short gszt.00419B77
00419B61   lea eax,dword ptr ss:[ebp-1C]
00419B64   push eax                   ; /pMsg
00419B65   call dword ptr ds:[42D1D4]       ; /TranslateMessage
00419B6B   lea ecx,dword ptr ss:[ebp-1C]
00419B6E   push ecx                   ; /pMsg
00419B6F   call dword ptr ds:[42D1D8]       ; /DispatchMessageA
00419B75   jmp short gszt.00419B4D     ;-----跳到上面

花3秒钟看一下,上面就是往消息队列放消息。这样看来对于柱形参数菜单软件的处理流程就是:
1、 修改该菜单内容;
2、 调用InvalidateRect 函数;
3、 继续日常消息传送。
由于这个菜单点下以后,可以看到经过计算的柱形股票数据分析。但上面即没有什么高深的计算,连柱形图显示都没发现。
那么先泡一包方便面,郁闷10分钟………..
吃完面,继续研究。既然这个菜单没有什么金子,目前来说,可以考虑一下处理画坐标系的方法,比如可以拦截一下GDI的画图函数,看看软件怎么画那个坐标系。
但翻了一下GDI教程,发现InvalidateRect函数可以引发WM_PAINT消息,从而导致窗口重画。那么看来就是这个函数引发窗体上的坐标系重画。既然如此,下一步就是找到处理WM_PAINT消息的地方。
寻找窗体消息处理,同样有N种方法。但我又要说我不会脱壳,也不会用OD的高级功能。因此慢慢来吧。
这个软件的典型SDK风格让我这个没写过SDK的人也忍不住去看典型的窗体消息处理:

Mov   @stWndClass.lpfnWndProc,offset _ProcWinMain
Mov   @stWndClass.hbrBackground,COLOR_WINDOW + 1
Mov   @stWndClass.lpszClassName,offset szClassName
Invoke RegisterClassEx,addr @stWndClass
invoke CreateWindowEx,WS_EX_CLIENTEDGE,offset……(略几个参数)

既然如此,就应该找到CreateWindowEx,然后看一下上面的offset _ProcWinMain。由于N多程序喜欢把关键界面采用子窗口处理,因此还要防止上面的InvalidateRect是处理的子窗口,那样就需要从N个CreateWindowEx函数中找到创建目标子窗口的那一个。
窗口的创建一般在程序初始化时候进行,因此退出软件。用OD加载软件,提示都不理会,中断在入口后,用Ctrl+A让OD分析一下代码。下CreateWindowEx断点,F9运行OD。被中断在CreateWindowEx,返回到主程序领空的调用处(注意用Ctrl+A分析一下那些杂乱op就会显示代码)。
00419C25   push eax                     ; │Width
00419C26   push 32                     ; │Y = 32 (50.)
00419C28   push 64                     ; │X = 64 (100.)
00419C2A   push 0CF0000                
00419C2F   push gszt.0043B030            
00419C34   push gszt.0043B048            
00419C39   push 0                     ; │ExtStyle = 0
00419C3B   call dword ptr ds:[42D214]         ; /CreateWindowExA
00419C41   mov dword ptr ss:[ebp-4],eax

考虑到有子窗口的问题,为了确定该次CreateWindowEx是否创建的是画坐标系的窗口,因此记下返回的窗口句柄值:018D0238 H。
F9继续运行,用窗口spy之类的东东查看一下坐标系所在窗口的句柄,也是018D0238 H,其实坐标系所在窗口就是主窗体。这样,上面分析的WM_PAINT消息触发的就是主窗体,然后重画主窗体上的坐标系。
再温习一下上面的那几句注册窗口的汇编代码,用OD重新加载一下软件,在启动时候找到这个CreateWindowEx上面的RegisterClass,就是这个窗口的注册函数:

00419BD5   mov dword ptr ss:[ebp-8],gszt.0043B014    
00419BDC   mov dword ptr ss:[ebp-4],gszt.0043B020    
00419BE3   lea edx,dword ptr ss:[ebp-28]
00419BE6   push edx               ; /pWndClass
00419BE7   call dword ptr ds:[42D1CC]   ; /RegisterClassA
00419BED   and eax,0FFFF

上面的00419BE6   push edx 指向窗口的注册信息结构,查一下MSDN,结构定义如下:

typedef struct _WNDCLASS {
  UINT   style;
  WNDPROC lpfnWndProc;   窗口消息函数地址
  int   cbClsExtra;
  int   cbWndExtra;
  HANDLE hInstance;
  HICON   hIcon;
  HCURSOR hCursor;
  HBRUSH hbrBackground;
  LPCTSTR lpszMenuName;
  LPCTSTR lpszClassName;
} WNDCLASS

该结构第2个word就是窗口消息处理函数。结构如下:
0012FEE4   08 00 00 00 6D 9C 41 00 00 00 00 00 00 00 00 00   ...m淎.........

根据第2个字节,找到地址 419C6D 。代码如下:

00419C6D   push ebp
00419C6E   mov ebp,esp
00419C70   sub esp,100
00419C76   push esi
00419C77   movsx eax,byte ptr ds:[446DC9]
00419C7E   test eax,eax
00419C80   je short gszt.00419D01     ;此处跳
00419C82   mov ecx,dword ptr ss:[ebp+C]
00419C85   mov dword ptr ss:[ebp-74],ecx
00419C88   cmp dword ptr ss:[ebp-74],114
00419C8F   ja short gszt.00419CBF
00419C91   cmp dword ptr ss:[ebp-74],114      
00419C98   je short gszt.00419CDC
00419C9A   cmp dword ptr ss:[ebp-74],102
00419CA1   ja short gszt.00419CB4
00419CA3   cmp dword ptr ss:[ebp-74],100
00419CAA   jnb short gszt.00419CDC
00419CAC   cmp dword ptr ss:[ebp-74],10
00419CB0   je short gszt.00419CDC
00419CB2   jmp short gszt.00419CE6
…………………….

上面的比较,就是比较熟悉的消息比较。如果在最开始地方(00419C6D处)下断点,就会被频繁中断,因为是消息处理最开头嘛。
尝试在几个跳转比较点下断点,会发现上面的

00419C80   je short gszt.00419D01     ;此处跳

会跳转,因此,观察跳转处:

00419D01   mov eax,dword ptr ss:[ebp+C]   ;获取传来的消息
00419D04   mov dword ptr ss:[ebp-78],eax
00419D07   cmp dword ptr ss:[ebp-78],111
00419D0E   ja short gszt.00419D7E
00419D10   cmp dword ptr ss:[ebp-78],111
00419D17   je gszt.0041A4DB
00419D1D   cmp dword ptr ss:[ebp-78],4E
00419D21   ja short gszt.00419D52
00419D23   cmp dword ptr ss:[ebp-78],4E
00419D27   je gszt.0041A2A4
00419D2D   mov ecx,dword ptr ss:[ebp-78]
00419D30   sub ecx,1                   ; wm_paint = 0F h
00419D33   mov dword ptr ss:[ebp-78],ecx
00419D36   cmp dword ptr ss:[ebp-78],0E
00419D3A   ja gszt.00422E98
00419D40   mov eax,dword ptr ss:[ebp-78]     ; 如果wm_paint,此处为 0E h
00419D43   xor edx,edx
00419D45   mov dl,byte ptr ds:[eax+422ECD]
00419D4B   jmp dword ptr ds:[edx*4+422EB9]

由于我们关心WM_PAINT消息,在windows.h查一下该消息值为:0F H。
上面的00419D01   mov eax,dword ptr ss:[ebp+C] 用来获取传来的消息。我们假定该处获得WM_PAINT消息,也就是0FH。观察一下代码的流向。这里很好观察,但是为了避免出错,你可以把传来的参数硬性改成0FH。到了

00419D4B   jmp dword ptr ds:[edx*4+422EB9]

再往下:

0041EFEA   mov eax,dword ptr ds:[43CFD4]
0041EFEF   cmp eax,dword ptr ds:[43CFDC]
0041EFF5   jge short gszt.0041F00B

然后不用细说,F8执行几步就到了处理数据的地方,如果再往下跟踪,就是GDI画图函数,用来将计算出来的数据显示在坐标系里面。
下面就正式登堂入室,可以分析软件如何计算数据,然后将这些数据以图形形式打印在界面上。
分析这些入口点,一般来说需要一些技巧,但是更需要扎实的功底,我一般不太乐意看一些花哨的插件或者高级的用法,当然并不是说那样东西不好,只不过是说采用一些基础的知识,其实可以达到那些目的。基础的知识才是我们需要加强的。
再往下就可以分析软件的算法了。

很多程序的核心算法,都是枯燥的数据运算,如果要搞清楚他们,关键有3点:

1、 找到他们所在的地方;
  这就是本文所讲的东西。

2、 可以理解整个运算过程;
  这是枯燥的跟踪调试,也是最消耗时间很反映个人基础的过程;

3、 理解算法的逻辑。
  看起来这一点没必要,我以前也这么认为。但是后来发现,有些东西,即使理解运算过程,也很难理解运算逻辑,也就是不晓得为什么要那么运算。要做到这一点,就靠触类旁通的天赋+平时的积累。

  因此,诚如一位大侠所说:“终极逆向工程,在于理解对方的整个运算流程和逻辑思维。”吾等小辈,唯有孜孜不倦,才有可能达到终极境界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值