附件:2.rar 

研究TlsCallBack,本例子按照xp sp2的环境而写,98下肯定game over, 2000下没有测过,希望大家告知结果,谢谢

程序编译出来没有TLS表,输入表的区段中找了个块空间,硬加了个表进去

设置OD停在系统断点,截入2.exe
可以对RtlImageDirectoryEntryToData下断 (ntdll.dll中的)
找到这里
  7C9484E8           50                push eax                                 //一开始ebx是0, 和ebx比较就是和0比较
  7C9484E9           6A 09             push 9
  7C9484EB           6A 01             push 1
  7C9484ED           8B7D 08           mov edi,dword ptr ss:[ebp+8]
  7C9484F0           57                push edi
  7C9484F1           E8 6083FEFF       call ntdll.RtlImageDirectoryEntryToData  //返回值EAX = 4020C0, 是TLS表的始址
  7C9484F6           33DB              xor ebx,ebx
  7C9484F8           895D FC           mov dword ptr ss:[ebp-4],ebx
  7C9484FB           3BC3              cmp eax,ebx                              //检测TLS始址是否为0, 这里有, 所以不跳              
  7C9484FD           74 20             je short ntdll.7C94851F
  7C9484FF           8B70 0C           mov esi,dword ptr ds:[eax+C]             //[4020C0+0C]叫做TlsCallBack, 我这里值是4020E0
  7C948502           8975 E0           mov dword ptr ss:[ebp-20],esi            //4020E0保存在变量A
  7C948505           3BF3              cmp esi,ebx                              //显然4020E0 !=0, 所以不跳
  7C948507           74 16             je short ntdll.7C94851F
  7C948509           381D 21C1997C     cmp byte ptr ds:[7C99C121],bl            //不知道反正不跳
  7C94850F           0F85 2F2F0100     jnz ntdll.7C95B444
  7C948515           8B06              mov eax,dword ptr ds:[esi]               //取出4020E0中的值, 值是401040,即CallBack入口
  7C948517           3BC3              cmp eax,ebx                              //如果不等于0就跳走,delphi的程序这里都是0
  7C948519           0F85 3A2F0100     jnz ntdll.7C95B459                       //例子中我硬加了值401040, 所以要跳走
  7C94851F           834D FC FF        or dword ptr ss:[ebp-4],FFFFFFFF
  7C948523           E8 DA68FEFF       call ntdll.7C92EE02
  7C948528           C2 0800           retn 8


  7C95B459           8945 E4           mov dword ptr ss:[ebp-1C],eax            //上面因为401040不为0, 所以跳到这里
  7C95B45C           83C6 04           add esi,4                                // 4020E0+4
  7C95B45F           8975 E0           mov dword ptr ss:[ebp-20],esi            // 4020E4保存在变量A
  7C95B462           381D 21C1997C     cmp byte ptr ds:[7C99C121],bl            // 不知道和上面一样,上面jnz不跳,这里je跳
  7C95B468           74 0F             je short ntdll.7C95B479                  
  7C95B46A           50                push eax
  7C95B46B           57                push edi
  7C95B46C           68 20B5957C       push ntdll.7C95B520  
  7C95B471           E8 7A4FFFFF       call ntdll.DbgPrint
  7C95B476           83C4 0C           add esp,0C
  7C95B479           53                push ebx
  7C95B47A           FF75 0C           push dword ptr ss:[ebp+C]
  7C95B47D           57                push edi
  7C95B47E           FF75 E4           push dword ptr ss:[ebp-1C]
  7C95B481           E8 0D5DFCFF       call ntdll.7C921193                      // 跟进去, 看下面
  7C95B486         ^ E9 8AD0FEFF       jmp ntdll.7C948515                       // 跳上去检测下一个CallBack是否存在


  7C921193           55                push ebp
  7C921194           8BEC              mov ebp,esp
  7C921196           56                push esi
  7C921197           57                push edi
  7C921198           53                push ebx
  7C921199           8BF4              mov esi,esp                              //保存Stack
  7C92119B           FF75 14           push dword ptr ss:[ebp+14]
  7C92119E           FF75 10           push dword ptr ss:[ebp+10]
  7C9211A1           FF75 0C           push dword ptr ss:[ebp+C]
  7C9211A4           FF55 08           call dword ptr ss:[ebp+8]                // 第一次, Call 进401040
  7C9211A7           8BE6              mov esp,esi                              //取回Stack, 可见TlsCall中不破坏esi, 不异常出来,就能正常继续下一Call
  7C9211A9           5B                pop ebx
  7C9211AA           5F                pop edi
  7C9211AB           5E                pop esi
  7C9211AC           5D                pop ebp
  7C9211AD           C2 1000           retn 10



我例子中的的EP是401000,为了说明问题,我把那儿清空了,现在ep前,因为TlsCallBack到了401040

  00401040           C705 E4204000 801>mov dword ptr ds:[4020E4],2.00401080                 ; 将下一个CallBack的入口写入
  0040104A           6A 00             push 0
  0040104C           68 20304000       push 2.00403020                                      ; ASCII "From Tls CallBack 1"
  00401051           68 00304000       push 2.00403000                                      ; ASCII "Tls CallBack 2 Actived"
  00401056           6A 00             push 0
  00401058           FF15 08204000     call dword ptr ds:[<&USER32.MessageBoxA>]            ; USER32.MessageBoxA
  0040105E           C3                retn


再来看一下Tls表,表始址是4020C0, +0C处是4020E0称为TlsCallBack, 4020E0指向401040, 这个地址会比ep先获得执行权(98不带)
004020C0  00 00 00 00  00 00 00 00  D0 20 40 00  E0 20 40 00  ........?@.?@.        
004020D0  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
004020E0  40 10 40 00  00 00 00 00  00 00 00 00  00 00 00 00  @@.............
401040那句话完了以后,变成下面, 我故意动态的将第二个TlsCallBack入口401080写入,因为这样比较好玩
004020C0  00 00 00 00  00 00 00 00  D0 20 40 00  E0 20 40 00  ........?@.?@.
004020D0  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
004020E0  40 10 40 00  80 10 40 00  00 00 00 00  00 00 00 00  @@.@.........

40105E 返回后
  7C95B486         ^ E9 8AD0FEFF       jmp ntdll.7C948515                       // 跳上去检测下一个CallBack是否存在
由于401080存在,因此,就进入了第二个TlsCallBack



  00401080           56                push esi                                 // esi是外面保存的Stack值,不要破坏比较好,压栈保存
  00401081           8BF4              mov esi,esp
  00401083           AD                lods dword ptr ds:[esi]                  // 从Stack向下寻找ep
  00401084           3D 00104000       cmp eax,2.401000                         // 我例子中的ep是401000
  00401089         ^ 75 F8             jnz short 2.00401083                     // 不等就跳上去继续找
  0040108B           05 C0000000       add eax,0C0                              // 找到了,401000+c0 = 4010c0
  00401090           8946 FC           mov dword ptr ds:[esi-4],eax             // 新的ep 4010C0 写回Stack中的地方
  00401093           6A 00             push 0
  00401095           68 40304000       push 2.00403040                                      ; ASCII "From Tls CallBack 2"
  0040109A           68 60304000       push 2.00403060                                      ; ASCII "Change EP to 4010C0"
  0040109F           6A 00             push 0
  004010A1           FF15 08204000     call dword ptr ds:[<&USER32.MessageBoxA>]            ; USER32.MessageBoxA
  004010A7           5E                pop esi                                  // esi出来
  004010A8           C3                retn


在TlsCallBack中可以看到在Stack中看到ep的值, 并且这个ep就是有效, 因此可以动态的改掉
这里4010A8 retn 出来后,下一个TlsCallBack的值是0,因此这里TlsCallBack的工作都完成了, 现在的ep是 4010C0



来到ep
  004010C0           C705 E0204000 001>mov dword ptr ds:[4020E0],2.00401100     //将第一个TlsCallBack的入口改掉,以后还会去
  004010CA           C705 E4204000 000>mov dword ptr ds:[4020E4],0              //第二个TlsCallBack入口清0, 不需要用了
  004010D4           6A 00             push 0
  004010D6           68 80304000       push 2.00403080                                      ; ASCII "From new EP"
  004010DB           68 90304000       push 2.00403090                                      ; ASCII "Change Tls CallBack 1 and ExitProcess"
  004010E0           6A 00             push 0
  004010E2           FF15 08204000     call dword ptr ds:[<&USER32.MessageBoxA>]            ; USER32.MessageBoxA
  004010E8           6A 00             push 0
  004010EA           FF15 00204000     call dword ptr ds:[<&KERNEL32.ExitProcess>]          ; kernel32.ExitProcess

Call了ExitProcess并没有完,跟进看看就知道了
进去后第一个CallF7,看到下面
  7C81CA2B           8B35 FC13807C     mov esi,dword ptr ds:[<&ntdll.NtTerminateProcess>]   ; ntdll.ZwTerminateProcess
  7C81CA31           FFD6              call esi
  7C81CA33           8985 20FFFFFF     mov dword ptr ss:[ebp-E0],eax
  7C81CA39           E8 82000000       call <jmp.&ntdll.LdrShutdownProcess>     //这个要F7

一路F8到 到 jmp short 7C943EBC,这里一大段是call 进所有的dll DllMain带Detach
从 je 7C943F6B 这句话出来, 再F8十几行到 call 7C9484D9, 这个进去就是最上面的处理TlsCallBack那大段了



因此ExitProcess函数仍会跳进TlsCallBack, 现在第1个TlsCallBack的入口是401100
  00401100           68 00002000       push 200000                              //如果这里是0,MessageBox会弹不出来
  00401105           68 C0304000       push 2.004030C0                                           ; ASCII "From Tls Call Back 1"
  0040110A           68 E0304000       push 2.004030E0                                           ; ASCII "App had Called ExitProcess"
  0040110F           6A 00             push 0
  00401111           FF15 08204000     call dword ptr ds:[<&USER32.MessageBoxA>]                 ; USER32.MessageBoxA
  00401117           C3                retn


关于最后一个MessageBox, 我猜由于是已调用NtTerminateProcess所以flag是0的话弹不出来,好在可以用 MB_SERVICE_NOTIFICATION 


这个完了以后, 才算真正的完了, 若干F8跑出LdrShutdownProcess,再下面一个Call就是 sysenter