首页
社区
课程
招聘
奇妙的跳转[原创]
发表于: 2006-9-14 12:44 25904

奇妙的跳转[原创]

2006-9-14 12:44
25904

【文章标题】: 奇妙的跳转
【文章作者】: coldpine
【软件名称】: SV_KeygenMe.exe
【下载地址】: http://www.crackmes.de/users/sv_reverser/sv_keygenme/
【编写语言】: masm
【使用工具】: od & calc.exe
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  用od加载,对GetDlgItemTextA函数下断点,并输入
  name:heminrui
  code:1234567890123456(经过跟踪发现要16位序列号)
  00401229   .  6A 28         PUSH    28                               ;
  0040122B   .  68 68344000   PUSH    SV_Keyge.00403468                ;
  00401230   .  68 EB030000   PUSH    3EB                              ;
  00401235   .  FF75 08       PUSH    DWORD PTR SS:[EBP+8]             ;
  00401238   .  E8 0B010000   CALL    <JMP.&user32.GetDlgItemTextA>    ; 获取输入的name
  0040123D   .  6A 28         PUSH    28                               ;
  0040123F   .  68 68304000   PUSH    SV_Keyge.00403068                ;
  00401244   .  68 EC030000   PUSH    3EC                              ;
  00401249   .  FF75 08       PUSH    DWORD PTR SS:[EBP+8]             ;
  0040124C   .  E8 F7000000   CALL    <JMP.&user32.GetDlgItemTextA>    ; 获取输入的code
  00401251   .  68 68304000   PUSH    SV_Keyge.00403068                ;
  00401256   .  E8 53010000   CALL    <JMP.&kernel32.lstrlenA>         ; code代码的长度
  0040125B   .  83F8 10       CMP     EAX, 10                          ;判断是不是16位,如果不是的,就跳出
  0040125E      75 64         JNZ     SHORT SV_Keyge.004012C4
  00401260   .  E8 D3FDFFFF   CALL    SV_Keyge.00401038        ;关键call,计算序列号用的函数
  
  跟进子函数
  功能:取name每位字符的ascii码和code的两位数字进行异或
  ----------------------------------------------------------------------------------------------
  00401038  /$  8D1D 68344000 LEA     EBX, DWORD PTR DS:[403468]       ;用户名地址进ebx
  0040103E  |.  8D35 68304000 LEA     ESI, DWORD PTR DS:[403068]       ;序列号地址进esi
  00401044  |.  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  0040104A  |.  EB 2A         JMP     SHORT SV_Keyge.00401076
  0040104C  |>  6A 03         /PUSH    3                               ; /n = 3
  0040104E  |.  56            |PUSH    ESI                             ; |String2
  0040104F  |.  68 68364000   |PUSH    SV_Keyge.00403668               ; |String1 = SV_Keyge.00403668
  00401054  |.  E8 4F030000   |CALL    <JMP.&kernel32.lstrcpynA>       ; \lstrcpynA
  00401059  |.  68 68364000   |PUSH    SV_Keyge.00403668               ;取code两位进缓冲区
  0040105E  |.  E8 51030000   |CALL    SV_Keyge.004013B4
  00401063  |.  3203          |XOR     AL, BYTE PTR DS:[EBX]           ;取code前两位和name的第一位的ascii码值异或
  00401065  |.  8807          |MOV     BYTE PTR DS:[EDI], AL           ;保存结果
  00401067  |.  46            |INC     ESI                             
  00401068  |.  46            |INC     ESI               ;code后两位的地址
  00401069  |.  43            |INC     EBX               ;取name后一位的地址
  0040106A  |.  803B 00       |CMP     BYTE PTR DS:[EBX], 0            ;没有取完,就继续取完,取玩的话,再重新取一遍
  0040106D  |.  75 06         |JNZ     SHORT SV_Keyge.00401075
  0040106F  |.  8D1D 68344000 |LEA     EBX, DWORD PTR DS:[403468]
  00401075  |>  47            |INC     EDI
  00401076  |>  803E 00        CMP     BYTE PTR DS:[ESI], 0      ;比较code取完了没有            
  00401079  |.^ 75 D1         \JNZ     SHORT SV_Keyge.0040104C
  0040107B  \.  C3            RET
  
  
  ----------------------------------------------------------------------------------------------------
  
  
  
  00401265   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  0040126B      FFD7          CALL    EDI
  0040126D   .  6A 00         PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
  0040126F   .  68 3B304000   PUSH    SV_Keyge.0040303B                ; |then  ?
  00401274   .  68 1C304000   PUSH    SV_Keyge.0040301C                ; |try again
  00401279   .  6A 00         PUSH    0                                ; |hOwner = NULL
  0040127B   .  E8 E6000000   CALL    <JMP.&user32.MessageBoxA>        ; \MessageBoxA
  00401280   .  EB 42         JMP     SHORT SV_Keyge.004012C4
  00401282   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  00401288   .  83C7 04       ADD     EDI, 4
  0040128B   .  8B07          MOV     EAX, DWORD PTR DS:[EDI]
  0040128D      3B05 90364000 CMP     EAX, DWORD PTR DS:[403690]
  00401293      75 1C         JNZ     SHORT SV_Keyge.004012B1
  00401295   .  E8 60000000   CALL    SV_Keyge.004012FA
  0040129A   .  68 E9030000   PUSH    3E9                              ; /ControlID = 3E9 (1001.)
  0040129F   .  FF75 08       PUSH    DWORD PTR SS:[EBP+8]             ; |hWnd
  004012A2   .  E8 9B000000   CALL    <JMP.&user32.GetDlgItem>         ; \GetDlgItem
  004012A7   .  6A 00         PUSH    0                                ; /Enable = FALSE
  004012A9   .  50            PUSH    EAX                              ; |hWnd
  004012AA   .  E8 8D000000   CALL    <JMP.&user32.EnableWindow>       ; \EnableWindow
  004012AF   .  EB 13         JMP     SHORT SV_Keyge.004012C4
  004012B1   >  6A 00         PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
  004012B3   .  68 3B304000   PUSH    SV_Keyge.0040303B                ; |then  ?
  004012B8   .  68 1C304000   PUSH    SV_Keyge.0040301C                ; |try again
  004012BD   .  6A 00         PUSH    0                                ; |hOwner = NULL
  004012BF   .  E8 A2000000   CALL    <JMP.&user32.MessageBoxA>        ; \MessageBoxA
  004012C4   >  FF75 EC       PUSH    DWORD PTR SS:[EBP-14]
  004012C7   .  64:8F05 00000>POP     DWORD PTR FS:[0]
  004012CE   .  EB 0D         JMP     SHORT SV_Keyge.004012DD
  004012D0   >  66:3D EA03    CMP     AX, 3EA
  004012D4   .  75 07         JNZ     SHORT SV_Keyge.004012DD
  004012D6   .  6A 00         PUSH    0                                ; /ExitCode = 0
  004012D8   .  E8 8F000000   CALL    <JMP.&user32.PostQuitMessage>    ; \PostQuitMessage
  004012DD   >  EB 15         JMP     SHORT SV_Keyge.004012F4
  004012DF   >  FF75 14       PUSH    DWORD PTR SS:[EBP+14]            ; /lParam
  004012E2   .  FF75 10       PUSH    DWORD PTR SS:[EBP+10]            ; |wParam
  004012E5   .  FF75 0C       PUSH    DWORD PTR SS:[EBP+C]             ; |Message
  004012E8   .  FF75 08       PUSH    DWORD PTR SS:[EBP+8]             ; |hWnd
  004012EB   .  E8 40000000   CALL    <JMP.&user32.DefWindowProcA>     ; \DefWindowProcA
  004012F0   .  C9            LEAVE
  004012F1   .  C2 1000       RET     10
  004012F4   >  33C0          XOR     EAX, EAX
  004012F6   .  C9            LEAVE
  004012F7   .  C2 1000       RET     10
  
  
--------------------------------------------------------------------------------
【分析总结】
  这个软件我运行了之后通过调试,发现根本没有成功的对话框
  即使code是16位的,仍然一运行就跳到try again对话框上去了.
  但是仔细看了一下代码
  00401265   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  0040126B      FFD7          CALL    EDI
  0040126D   .  6A 00         PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
  0040126F   .  68 3B304000   PUSH    SV_Keyge.0040303B                ; |then  ?
  00401274   .  68 1C304000   PUSH    SV_Keyge.0040301C                ; |try again
  00401279   .  6A 00         PUSH    0                                ; |hOwner = NULL
  0040127B   .  E8 E6000000   CALL    <JMP.&user32.MessageBoxA>        ; \MessageBoxA
  00401280   .  EB 42         JMP     SHORT SV_Keyge.004012C4
  00401282   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  00401288   .  83C7 04       ADD     EDI, 4
  0040128B   .  8B07          MOV     EAX, DWORD PTR DS:[EDI]
  0040128D      3B05 90364000 CMP     EAX, DWORD PTR DS:[403690]
  00401293      75 1C         JNZ     SHORT SV_Keyge.004012B1
  00401295   .  E8 60000000   CALL    SV_Keyge.004012FA
  0040129A   .  68 E9030000   PUSH    3E9                              ; /ControlID = 3E9 (1001.)
  0040129F   .  FF75 08       PUSH    DWORD PTR SS:[EBP+8]             ; |hWnd
  004012A2   .  E8 9B000000   CALL    <JMP.&user32.GetDlgItem>         ; \GetDlgItem
  004012A7   .  6A 00         PUSH    0                                ; /Enable = FALSE
  004012A9   .  50            PUSH    EAX                              ; |hWnd
  004012AA   .  E8 8D000000   CALL    <JMP.&user32.EnableWindow>       ; \EnableWindow
  004012AF   .  EB 13         JMP     SHORT SV_Keyge.004012C4
  004012B1   >  6A 00         PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
  004012B3   .  68 3B304000   PUSH    SV_Keyge.0040303B                ; |then  ?
  004012B8   .  68 1C304000   PUSH    SV_Keyge.0040301C                ; |try again
  004012BD   .  6A 00         PUSH    0                                ; |hOwner = NULL
  004012BF   .  E8 A2000000   CALL    <JMP.&user32.MessageBoxA>        ; \MessageBoxA
  发现里面有一个EnableWindow函数,这几天我一直被那几个变灰的按钮搞的头晕,我猜想,是不是
  注册成功就跳转到这个EnableWindow函数上呢?但是查了半天没有看见能跳到EnableWindow这个函数
  上的跳转语句.但是我注意到了这个语句
  00401265   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  0040126B      FFD7          CALL    EDI
  call edi 这个语句应该是调用数据段中的数据,于是我想肯定是通过异或计算出来的结果
  通过call,电脑就把那些数据理解成代码,而且应该是个段间跳转吧!
  于是我通过修改汇编指令:
  首先转到DS:[403268]
  然后修改汇编指令jmp long 401282,看见对应的hex数据变成了E915E0FFFF
  然后单步执行又跳回代码段
  
  00401282   .  8D3D 68324000 LEA     EDI, DWORD PTR DS:[403268]
  00401288   .  83C7 04       ADD     EDI, 4
  0040128B   .  8B07          MOV     EAX, DWORD PTR DS:[EDI]
  0040128D      3B05 90364000 CMP     EAX, DWORD PTR DS:[403690]
  00401293      75 1C         JNZ     SHORT SV_Keyge.004012B1
  
  只是后面有一个比较语句取出计算出来的数据后四个字节
  和DS:[403690]这个地方进行双字(DWORD)比较,我就纳闷了,这个DS:[403690]
  好像在计算序列号的时候并没有出现过,我通过向上查看,终于看见了DS:[403690]出现
  00401218   .  C705 90364000>MOV     DWORD PTR DS:[403690], 0FFFFFF
  00401222   .  C605 68324000>MOV     BYTE PTR DS:[403268], 0
  原来DS:[403690]的内容是00ffffff
  这个说明了通过name和code计算的结果后面的四个字节中应该有00ffffff
  否则的话也跳出那个按钮变灰的函数.
  
  
  好了,弄清楚了算法:就是将name和code两位异或,结果前面刚好能
  组成一个段间跳转语句,后面的四位刚好是00ffffff
  由于按照双字取出,所以实际上要的是ffffff00
  所以最终name和code异或的结果必须是
  E915E0FF  FFFFFF00
   
  
  现在就开始计算正确的序列号:由于计算过程只有一个异或的过程很简单,并且
  异或运行的逆运算也是异或,所以:
  h 的ascii码值是68   xor   e9     =     81
  e 的ascii码值是65   xor   15     =     70
  m 的ascii码值是6D   xor   e0     =     8d
  i 的ascii码值是69   xor   ff     =     96
  n 的ascii码值是6E   xor   ff     =     91
  r 的ascii码值是72   xor   ff     =     8d
  u 的ascii码值是75   xor   ff     =     8a
  i 的ascii码值是69   xor   00     =     69
  组在一起就是code:81708d96918d8a69 由于只是比较数字,所以不分大小写.
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年09月14日 12:41:33


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (27)
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
2
斑竹暂时不要加精,没有这么简单。

提示你一下:这个CrackMe包含SEH异常处理,正确的序列号必须在与用户名异或后成为引发特定异常的机器指令序列,而且第二个双字必须等于异常代码。

再仔细想想,建议你熟悉一下SEH异常处理的机制。

PS:汇编写的程序你没有让它进IDA屠宰场那真是太遗憾了
2006-9-14 12:52
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
3
最初由 冲天剑 发布
建议你熟悉一下SEH异常处理的机制。
........

多谢指点,这个seh异常,确实我不太熟悉!!

另外一点:你的意思是说对汇编语言的程序用ida更简单
     吗? 
     只不过我现在一直用od调试,我想先玩熟悉od再说
          对ida一直没试过,我想等我熟悉od之后会试试ida
         的.想问一下,难道注册成功了会弹出正确提示的对话框吗?
     感觉好像代码里不曾有这样的代码?请高手指点?
     
2006-9-14 13:00
0
雪    币: 270
活跃值: (176)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
4
在IDA里面差不多可以当源码读了...
2006-9-14 17:53
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
5
感觉最后注册成功应该也是这样的吧!~
连作者也说了no anti-debug 100% asm
我想应该没有什么问题的吧!!
我觉得利用异或计算当成代码处理,这本来就很
难想到的啊!!!!

当然我是菜鸟,所以见识可能少些!
希望高手指点说明!!!
2006-9-14 21:10
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
6
你有分析那个异常处理的回调函数吗?如果没有,先去分析401000处开始的那个过程。

另外,假设40126B处的call edi没有发生异常,那么它返回的下一句就会是一个“try again”的消息框,这显然是失败了。因此正确的注册码必定使得40126B处的call发生异常。那么应该是发生什么样的异常呢?再想想。

顺便说一下,你贴的代码只是一个局部,如果只从这一部分入手,就如同管中窥豹。看问题应该从全局来看。
2006-9-14 21:20
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
to 冲天剑:
我认为你的观点有点片面,好像只有发生异常才是正解,其实楼主用了Crackme作者本人都没有想到的方法进行了Crack.虽然楼主的确不太了解SEH,但是正是因为不了解让他闯出了一个新思路。我认为应该加精!
我认为crackme作者如果要充分利用SEH,就不能用这样代码:
004012C4   >  FF75 EC       PUSH    DWORD PTR SS:[EBP-14]
  004012C7   .  64:8F05 00000>POP     DWORD PTR FS:[0]
  004012CE   .  EB 0D         JMP     SHORT SV_Keyge.004012DD
来还原fs:[0],因为[ebp-14]是固定的。如果通过平衡堆栈来pop fs:[0],那么不异常就不能正确恢复fs:[0].也就是说直接jmp出来以后就会出错。
2006-9-14 21:53
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
8
@coldpine:
正如冲天剑所言,还要深入的研究一下SEH, 因为作者的要求是"...to have the good messagebox", 所以必须有一个msgbox才算成功, disable "GO"按钮不是注册成功与否的标志.
友情提示一下:
在用OD跟踪时注意堆栈中的"SEH Handler",并适时跟进SEH处理程序.我也不懂IDA.也是用OD跟的.
2006-9-14 22:19
0
雪    币: 234
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
Yeah,that's right !!
2006-9-14 22:26
0
雪    币: 256
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
10
感谢大家提供的一些信息,
尤其是冲天剑兄弟,我想我
如果解决的话,水平应该又
提高了一点的啊!!!!!
再次感谢!!!!!!!!
2006-9-15 07:13
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
11
首先说明一下:call edi是否发生异常,是这个问题的关键所在。如果不发生异常,除非你预先知道对“引用正确信息的指令序列”的解码循环在什么地方,然后设计一个jmp直接跳到那个地方。但是这样势必要修正call的返回地址,同时要修改eax的值等于密钥,不然执行结果还是错误的。这三个指令(一个jmp,一个修正返回地址和一个修改eax)加起来肯定超过8字节长,已经超过能够通过注册算法所生成的指令序列长度。

好吧,先提示一下:解码循环在地址401316处,你试验一下把地址403046起始的7个双字逐一与0C0000094h异或,然后反汇编,看看是什么结果。PS:0C0000094h是除零错误的异常代码。

在用OD跟踪时注意堆栈中的"SEH Handler",并适时跟进SEH处理程序.我也不懂IDA.也是用OD跟的.


遇到SEH的最好处理方法是绕过而不是跟进去,也就是自己在脑子里模拟SEH,因为调试器单步本身也是一种异常,碰到反单步(故意产生单步)的程序段就麻烦了。
2006-9-15 12:31
0
雪    币: 263
活跃值: (10)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
12
最初由 ikki 发布
在IDA里面差不多可以当源码读了...


妈的,看来真的要把 IDA 搞懂才行
2006-9-15 13:15
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
13
To 冲天剑: 多谢冲天剑兄的指点,不过我还是不太明白,"遇到SEH的最好处理方法是绕过而不是跟进去", 如果不跟进SEH的处理程序,你怎么知道它偷偷摸摸的干了些什么呢? "绕过"是怎么绕过, 绕到哪里去?  关于使用IDA日后还需要向冲天剑兄请教学习.
2006-9-15 21:20
0
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
14
简单的例子譬如:

_SEHandler proc   arg1, arg2, arg3, arg4

   mov  eax, arg3
   mov  [eax+CONTEXT.regEip], offset next1
   xor  eax, eax
   ret

_SEHandler endp

start:
   assume fs:nothing
   xor eax, eax
   push offset _SEHandler
   push fs:[eax]
   mov fs:[eax], esp
   inc dword ptr [eax]  ;产生异常

   ....

next1:
   pop fs:[eax]
   add esp, 4
   ret

   end start

通过分析_SEHandler过程我们知道它只是把线程的eip定向到地址next1,那么在调试器中跟踪到inc dword ptr [eax]这一句时就不要按F7,而是直接重置eip到地址next1处继续进行,这就是所谓绕过。当然更复杂的SEH回调函数可能还会干许多其他的工作,但是只要记住一点:如果函数的返回值是0(即ExceptionContinueExecution)的话,那么这个函数中进行的大部分操作都可以忽略,只需要知道它是如何修改全局变量以及arg3所指向的CONTEXT结构体(原线程运行环境)就足够了。
2006-9-15 21:52
0
雪    币: 221
活跃值: (20)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
15
不简单啊!!!
2006-9-15 22:09
0
雪    币: 342
活跃值: (318)
能力值: ( LV12,RANK:740 )
在线值:
发帖
回帖
粉丝
16
=>冲天剑:明白了,谢谢
2006-9-15 23:19
0
雪    币: 195
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
都是经验之谈呀,好好学习!
2006-10-11 12:43
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
06年的帖子,今天再看 依然很经典啊,多谢 @冲天剑 大牛!我要继续努力了!回去看异常处理!
2013-9-3 15:02
0
雪    币: 141
活跃值: (318)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
今天仔细分析一下,,学习一下
2013-9-11 22:00
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
mark  支持
2013-9-12 01:04
0
雪    币: 141
活跃值: (318)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
这个程序不简单奥。。。。。
2013-9-15 21:53
0
雪    币: 341
活跃值: (143)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
22
感觉很牛。。。。
2013-9-15 22:52
0
雪    币: 196
活跃值: (96)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
23
这个挺有意思
2013-11-26 17:07
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
嗯,只知其一不知其二呀。学习了。
2013-11-26 22:38
0
雪    币: 221
活跃值: (2311)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
贴个可用的注册码
lhg2lhg2
5FA890C2F86867F2 
2017-5-1 12:40
0
游客
登录 | 注册 方可回帖
返回
//