想要写个木马玩玩,什么功能也没有,就是监视键盘记录并发送到指定邮箱里。上网去找发邮件的例子,试试都不行,现在的邮箱都采取的验证的方式,以前的方法不能用了。正在无计可施时突然发现老罗的网站上一篇正是讲这个的(
http://www.luocong.com/articles/show_article.asp?Article_ID=21),一试之下大喜,对老罗的崇拜之情如滔滔江水。。。
可是没多久问题又来了,程序只有在连入网络发信才成功,当断线时发信会“非法操作”。(我用的98,不知别的系统怎样)这可不行,明摆着暴露自己嘛。可是把代码分析了半天也不知道错在哪儿。下面是部分代码(不好意思不能保持完整了):
.if eax == IDC_BUTTON_SEND
;以下是初始化 winsock :
invoke WSAStartup, 101h, addr wsadata
.if eax != NULL
invoke MessageBox, hWnd, addr szErrNoDll, addr szCaption, MB_OK or MB_ICONHAND
.else
invoke socket, AF_INET, SOCK_STREAM, 0
.if eax == INVALID_SOCKET
invoke MessageBox, hWnd, addr szErrSocket, addr szCaption, MB_OK or MB_ICONHAND
.else
mov hSocket, eax
mov sin.sin_family, AF_INET
invoke htons, 25
mov sin.sin_port, ax
invoke GetDlgItemText, hWnd, IDC_EDIT_SMTPSERVER, addr szSmtpServer, 255
invoke gethostbyname, addr szSmtpServer
mov eax, [eax + 12]
mov eax, [eax]
mov eax, [eax]
mov sin.sin_addr, eax
invoke connect, hSocket, addr sin, sizeof sin
.if eax < 0
invoke MessageBox, hWnd, addr szErrConnect, addr szCaption, MB_OK or MB_ICONHAND
.else
;下面是正常的发送过程了,省略
.endif
.endif
.endif
.endif
看上去没有问题嘛,把所有出错的地方都考虑进去了,为什么还会非法操作呢?我又用VC++用MFC写了一个,用封装好的CSocket类。源代码:
CSocket mySocket;
mySocket.Create(0,SOCK_STREAM);
if (!mySocket.Connect("smtp.163.com",25)) {
MessageBox("Cannot Connect!");
return;
}
strcpy(string,"EHLO smtp.163.com\r\n");
mySocket.Send(string,strlen(string));
这个没有问题,如果没有连网就会弹出"Cannot Connet!"。MFC是怎么做的呢,咱们把它反汇编看看:
00401504 . 6A 19 PUSH 19
00401506 . 68 E0314000 PUSH EMAIL2.004031E0 ; ASCII "smtp.163.com"
0040150B . 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
0040150F . E8 48060000 CALL <JMP.&MFC42.#2029> ;这个CALL是关键,进入
00401514 . 85C0 TEST EAX,EAX
00401516 . 75 39 JNZ SHORT EMAIL2.00401551 ;如果没有连网就不会跳
00401518 . 50 PUSH EAX
00401519 . 50 PUSH EAX
0040151A . 68 D0314000 PUSH EMAIL2.004031D0 ; ASCII "Cannot Connect!"
0040151F . 8BCD MOV ECX,EBP
00401521 . E8 30060000 CALL <JMP.&MFC42.#4224>
跟进CALL(对应上面语句中的mySocket.Connect):
6BC56684 > 55 PUSH EBP
6BC56685 8BEC MOV EBP,ESP
6BC56687 83EC 10 SUB ESP,10
6BC5668A 56 PUSH ESI
6BC5668B 6A 10 PUSH 10
6BC5668D 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
6BC56690 6A 00 PUSH 0
6BC56692 8BF1 MOV ESI,ECX
6BC56694 50 PUSH EAX
6BC56695 E8 AEB0FEFF CALL <JMP.&MSVCRT.memset>
6BC5669A 83C4 0C ADD ESP,0C
6BC5669D 66:C745 F0 0200 MOV WORD PTR SS:[EBP-10],2
6BC566A3 FF75 08 PUSH DWORD PTR SS:[EBP+8]
6BC566A6 E8 40000000 CALL MFC42.6BC566EB ; JMP to WSOCK32.inet_addr
6BC566AB 83F8 FF CMP EAX,-1
6BC566AE 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX
6BC566B1 75 1A JNZ SHORT MFC42.6BC566CD
6BC566B3 FF75 08 PUSH DWORD PTR SS:[EBP+8]
6BC566B6 E8 42000000 CALL MFC42.6BC566FD ; JMP to WSOCK32.gethostbyname
6BC566BB 85C0 TEST EAX,EAX ; 找到了,关键就是这里
6BC566BD 0F84 799C0700 JE MFC42.6BCD033C ; 如果失败就跳走了
6BC566C3 8B40 0C MOV EAX,DWORD PTR DS:[EAX+C]
6BC566C6 8B00 MOV EAX,DWORD PTR DS:[EAX]
6BC566C8 8B00 MOV EAX,DWORD PTR DS:[EAX]
6BC566CA 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX
6BC566CD FF75 0C PUSH DWORD PTR SS:[EBP+C]
6BC566D0 E8 8BFFFFFF CALL MFC42.6BC56660 ; JMP to WSOCK32.htons
前面都没有问题,过6BC566B6一句CALL gethostbyname的时候发现返回值EAX为0,然后就跳走了。而老罗的代码用OLLY跟一下是这样的:
0040111E |. 68 C6324000 PUSH SMTP.004032C6 ; /pWSAData = SMTP.004032C6
00401123 |. 68 01010000 PUSH 101 ; |RequestedVersion = 101 (1.1.)
00401128 |. E8 4F070000 CALL <JMP.&WSOCK32.#115> ; \WSAStartup
0040112D |. 0BC0 OR EAX,EAX
0040112F |. 74 19 JE SHORT SMTP.0040114A
00401131 |. 6A 10 PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401133 |. 68 0A304000 PUSH SMTP.0040300A ; |Title = "ESmtp demo by LC, 2002-10-12"
00401138 |. 68 27304000 PUSH SMTP.00403027 ; |Text = "装载winsock.dll时出错!"
0040113D |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401140 |. E8 19070000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401145 |. E9 1C060000 JMP SMTP.00401766
0040114A |> 6A 00 PUSH 0 ; /Protocol = IPPROTO_IP
0040114C |. 6A 01 PUSH 1 ; |Type = SOCK_STREAM
0040114E |. 6A 02 PUSH 2 ; |Family = AF_INET
00401150 |. E8 51070000 CALL <JMP.&WSOCK32.#23> ; \socket
00401155 |. 83F8 FF CMP EAX,-1
00401158 |. 75 19 JNZ SHORT SMTP.00401173 ;这里并没有出错而是跳过去了
0040115A |. 6A 10 PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040115C |. 68 0A304000 PUSH SMTP.0040300A ; |Title = "ESmtp demo by LC, 2002-10-12"
00401161 |. 68 3F304000 PUSH SMTP.0040303F ; |Text = "建立socket时出错!"
00401166 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401169 |. E8 F0060000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
0040116E |. E9 77050000 JMP SMTP.004016EA
00401173 |> A3 FC344000 MOV DWORD PTR DS:[4034FC],EAX
00401178 |. 66:C705 543440>MOV WORD PTR DS:[403454],2
00401181 |. 6A 19 PUSH 19 ; /HostSshort = 19
00401183 |. E8 0C070000 CALL <JMP.&WSOCK32.#9> ; \htons
00401188 |. 66:A3 56344000 MOV WORD PTR DS:[403456],AX
0040118E |. 68 FF000000 PUSH 0FF ; /Count = FF (255.)
00401193 |. 68 00354000 PUSH SMTP.00403500 ; |Buffer = SMTP.00403500
00401198 |. 68 B90B0000 PUSH 0BB9 ; |ControlID = BB9 (3001.)
0040119D |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011A0 |. E8 AD060000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004011A5 |. 68 00354000 PUSH SMTP.00403500 ; /Name = ""
004011AA |. E8 DF060000 CALL <JMP.&WSOCK32.#52> ; \gethostbyname
004011AF |. 8B40 0C MOV EAX,DWORD PTR DS:[EAX+C] ; EAX=0到这里会怎么样呢?:D
004011B2 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]
004011B4 |. 8B00 MOV EAX,DWORD PTR DS:[EAX]
004011B6 |. A3 58344000 MOV DWORD PTR DS:[403458],EAX
004011BB |. 6A 10 PUSH 10 ; /AddrLen = 10 (16.)
004011BD |. 68 54344000 PUSH SMTP.00403454 ; |pSockAddr = SMTP.00403454
004011C2 |. FF35 FC344000 PUSH DWORD PTR DS:[4034FC] ; |Socket = 0
004011C8 |. E8 BB060000 CALL <JMP.&WSOCK32.#4> ; \connect
004011CD |. 83F8 00 CMP EAX,0
004011D0 |. 73 19 JNB SHORT SMTP.004011EB
004011D2 |. 6A 10 PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004011D4 |. 68 0A304000 PUSH SMTP.0040300A ; |Title = "ESmtp demo by LC, 2002-10-12"
004011D9 |. 68 52304000 PUSH SMTP.00403052 ; |Text = "进行连接时出错!"
004011DE |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
004011E1 |. E8 78060000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
跟一下发现:在我们认为会出错的401150 这个CALL返回了正确的值,所以那个“建立Socket时出错”并不会跳出,而到4011AA这个CALL gethostbyname同样会返回0,因为没有进行处理接着进行MOV EAX,[EAX+C],想读DS:[0C]当然要死得很难看了:D
问题找到了,解决起来就容易多了,像MFC里一样在CALL gethostbyname后面加个判断就可以确保万无一失了(跟着M$没错)
高手肯定觉得这没什么了不起,但对于我来说,调试器真正用来给程序除错还是开天辟地头一遭,兴奋中。。。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)