[编程工具]masm32(9.00)+RadAsm2.2.0.9
[调试工具]OD1.10 VC++6.0
[调试平台]WinXP/SP2+QQ2006
在这里我首先感谢看雪给我们提供的许多强大的编程及调试工具,对于我们新手来说,有RadAsm这样的工具简直就是天大的幸运,RadAsm友好的界面与提示功能很容易上手,而OD的强大的分析能力让我调试更方便,OD所加的注释比我写的注释还要详细。
以前我一直用VC++,觉得他十分强大,只有你想不到的,没有他做不到的,在坛子里泡久了,发现也有VC不容易做到的事情。而坛子中的许多高手编写的东西都用了asm,我看起来有点吃力,就想学学asm编程。真是难者不会,会者不难,想起来容易,做起来难啊,我可是吃了许多苦头才完成的,谁叫我太$#@*呢。写出来只是想给一些想学asm编程而又没能开始的新手增加一点信心,我这样的大笨都开始学了,你们那些大聪怎么能不学呢?另外,也想听听大牛们给我指出不足,教我两招^O^.
去年的这个时候不小心中了流氓广告的招,它就是每隔几分钟就给所有QQ聊天者发一条广告。我觉得既好气又好玩,后来就用VC++6.0编了一个。现在我就把那个用VC写的东西,改写成asm语言的。用VC写的时候用了MFC,用静态链接竟然有200KB以上,用动态链接也有24KB之多,而用asm编出来只有不到6KB的大小。比一比,这VC&MFC在代码中加了多少垃圾啊!
代码中用到的都是一些初级的技术,但在给EnumWindows函数写EnumWindowsProc回调函数的时候,由于我没经验遇到了个大麻烦,花去了我半天的时间。试运行时发现,每当我点击“发送”按钮的时候程序就失去了响应,用OD和VC调试忙了半天也没有找到原因。后来我重启系统,再运行,系统提示非法操作,点击“调试”之后进入VC中停在系统领空中有 ds[edi] 字样的一行上,我恍然大悟:因为我在_EnumWindowsProc代码中用了edi寄存器而又没有用“uses edi”或“push edi/pop edi”保护edi!看来EnumWindows内部没有把重要的数据保存起来,应该是自己的东西自己保护吧?这个现在却要我来保护?我怎么知道你api内部哪些寄存器不能改变?总不能每次函数开始前先来一段push之类的吧?其它的一些函数就不是这样的呀,之前我在编写_SaveMsg proc时没保存edi不是一直没有出错嘛。我很想知道象EnumWindows函数这样自己不保护自己的api函数还有哪些。这不但可以防止编程出错,还可以用来保护我们的软件不被破解吧?!比如你的代码中用到了这样的函数,你就可以把edi的值取出来加密之后保存在某个地方,等你的函数退出之前再取出edi的值解密,如果软件未被破解,edi解出原来的值程序不会出问题,一但被破解,会解出错误的edi值,从而使程序变得很不稳定,不是失去响应就是非法操作的。我这样的想法是不是很荒唐?请大家指教。找到出错原因后我一气之下改了代码,不用那个edi了,也没去保护它,正常运行了。主要的代码在下面:
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include QQ自动群发器.inc
.code
start:
invoke InitCommonControls
invoke GetModuleHandle,NULL
invoke DialogBoxParam,eax,IDD_MAINDIALOG,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; _rand 随机数产生子程序
; 输入:要产生的随机数的最大值,输出:小于_dwMax的随机数
; 根据:
; 1. 数学公式 Rnd=(Rnd*I+J) mod K 循环回带生成 K 次以内不重复的
; 伪随机数,但K,I,J必须为素数
; 2. 2^(2n-1)-1 必定为素数(即2的奇数次方减1)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Random16 proc
push edx
mov eax,dwRandom
mov ecx,32768-1 ;2^15-1
mul ecx
add eax,2048-1 ;2^11-1
adc edx,0
mov ecx,2147483647 ;2^31-1
div ecx
mov eax,dwRandom
mov dwRandom,edx
and eax,0000ffffh
pop edx
ret
_Random16 endp
_rand proc _dwMax:DWORD ;uses ebx ecx edx
invoke _Random16
mov edx,eax
invoke _Random16
shl eax,16
or ax,dx
mov ecx,_dwMax
or ecx,ecx
jz @f
xor edx,edx
div ecx
mov eax,edx
@@:
ret
_rand endp
;*********************************************************************************
;初始化工作:读文件内容填入列表;设默认值;初始化随机数
;*********************************************************************************
_initDlg proc
LOCAL @hMem:HANDLE
LOCAL @hFile:HANDLE
LOCAL @pMem:HANDLE
LOCAL @FileSize:DWORD
LOCAL @hwnd:HWND
LOCAL @SizeRead:DWORD
LOCAL @str:DWORD
LOCAL systime:SYSTEMTIME
invoke GetDlgItem,hWndDlg,IDC_FILEMSG
mov @hwnd,eax
invoke SendMessage,eax, CB_ADDSTRING, 0,addr lpWRJ
invoke CreateFile,ADDR lpFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
.if eax==INVALID_HANDLE_VALUE
invoke SendMessage,@hwnd, CB_ADDSTRING, 0,addr lpString
.else
mov @hFile,eax
invoke GetFileSizeEx,@hFile,addr @FileSize
add @FileSize,4
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,@FileSize
mov @hMem,eax
invoke GlobalLock,eax
mov @pMem,eax
invoke ReadFile,@hFile,@pMem,@FileSize,ADDR @SizeRead,NULL
mov eax,@pMem
mov @str,eax
.while @SizeRead>0
.if dword ptr[eax]==0a0d0a0dh ;查找空行
mov dword ptr[eax],0
push eax
invoke SendMessage,@hwnd,CB_ADDSTRING,0,@str
pop eax
add eax,4
sub @SizeRead,4
mov @str,eax
.else
inc eax
dec @SizeRead
.endif
.endw
invoke SendMessage,@hwnd,CB_ADDSTRING,0,@str
invoke GlobalUnlock,@pMem
invoke GlobalFree,@hMem
invoke CloseHandle,@hFile
invoke SendMessage,@hwnd, CB_SETCURSEL, 0, 0
invoke CheckDlgButton,hWndDlg,IDC_RBN2,BST_CHECKED
.endif
invoke GetSystemTime,addr systime
push systime.wSecond ;初始化随机数
and eax,dwRandom
xor ax,systime.wMilliseconds
mov dwRandom,eax
ret
_initDlg endp
;*********************************************************************************
;响应 IDC_SAVE 按钮点击消息,保存文字到文件中
;*********************************************************************************
_SaveMsg proc ;uses edi
LOCAL @hFile :HANDLE
LOCAL @hMem :HANDLE
LOCAL @pMem :HANDLE
LOCAL @nWritten :DWORD
LOCAL @nLen :DWORD
invoke GetDlgItem,hWndDlg,IDC_SAVE
invoke EnableWindow,eax,FALSE
invoke GetDlgItem,hWndDlg,IDC_MESSAGE
invoke GetWindowTextLength,eax
or eax,eax
jz @F
add eax,5
mov @nLen,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,eax
mov @hMem,eax
invoke GlobalLock,eax
mov @pMem,eax
mov edi,eax
mov eax,0a0d0a0dh ;先加一个空行
cld
stosd
push edi
invoke GetDlgItemText,hWndDlg,IDC_MESSAGE,edi,@nLen ;读文本框内容到换行符之后
add eax,4
mov @nLen,eax
invoke GetDlgItem,hWndDlg,IDC_FILEMSG
push 0
push CB_ADDSTRING ;并且把读入的文本框内容加入列表中
push eax
call SendMessage
invoke CreateFile,ADDR lpFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL
.if eax!=INVALID_HANDLE_VALUE
mov @hFile,eax
invoke SetFilePointer,@hFile,0,0,FILE_END ;移动文件指针到文件尾
invoke WriteFile,@hFile,@pMem,@nLen,addr @nWritten,NULL ;内容添加到文件尾
invoke CloseHandle,@hFile
.endif
invoke GlobalUnlock,@pMem
invoke GlobalFree,@hMem
@@:
ret
_SaveMsg endp
;*********************************************************************************
;调用 EnumWindows 找所有的窗口
;*********************************************************************************
_SendMsg proc
LOCAL @nLen :DWORD
LOCAL @pMem :HANDLE
LOCAL @hMem :HANDLE
invoke GetDlgItem,hWndDlg,IDC_MESSAGE
invoke GetWindowTextLength,eax
.if eax==0
invoke _OnCBSelChange
.endif
inc eax
mov @nLen,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,eax
mov @hMem,eax
invoke GlobalLock,eax
mov @pMem,eax
invoke GetDlgItemText,hWndDlg,IDC_MESSAGE,eax,@nLen ;取文本框内容
invoke SetDlgItemText,hWndDlg,IDC_MESSAGE,NULL ;把文本框清空
invoke EnumWindows,_EnumWindowsProc,@pMem ;@pMem 中为要发的内容,所有窗口都找一遍才返回
invoke GlobalUnlock,@pMem
invoke GlobalFree,@hMem
ret
_SendMsg endp
;*********************************************************************************
;找到窗口,判断是否是QQ的聊天窗口,是则给他发送文字,不是就让EnumWindows继续找
;*********************************************************************************
_EnumWindowsProc proc hWndQQ:HWND,@pMem:DWORD ;uses edi 如果用了edi,必须要它,否则会出错,切记切记
LOCAL @hWndSend :HWND
LOCAL @hWndPro :HWND
invoke GetWindowText,hWndQQ,addr lpBuf,21 ;取窗口标题文字
mov ecx,eax
mov ax,0ebd3h ;"与"的内码倒置
lea edx,lpBuf
.while ax!=word ptr [edx] ;标题中有“与”这个字吗?
.if ecx==0
jmp FINDNEXTWND
.endif
dec ecx
inc edx
.endw
invoke FindWindowEx,hWndQQ,NULL,addr lpQQWindow,NULL ;对话框的类为“#32770”
mov @hWndPro,eax
invoke FindWindowEx,eax,NULL,addr lpQQDlg,NULL ;找QQ的窗口
invoke FindWindowEx,eax,NULL,addr lpQQMsg,NULL ;找QQ中写消息的文本框
.if eax!=NULL ;判断一下是否找到。如果前面的条件都满足,就应该能找到
invoke SendMessage,eax,EM_REPLACESEL,0,@pMem ;把要发的内容先送进这个文本框
invoke FindWindowEx,@hWndPro,NULL,NULL,NULL ;找那个“发送”按钮
.while eax!=NULL ;慢慢找吧^O^,里面的子窗口很多
mov @hWndSend,eax
invoke GetWindowText,eax,addr lpBuf,20 ;取子窗口内容
mov ecx,eax
mov ax,0a2b7h ;"发"的内码倒置
lea edx,lpBuf
.while ax!=word ptr [edx] ;有“发”字吗?
.if ecx==0
invoke FindWindowEx,@hWndPro,@hWndSend,NULL,NULL ;不是就找下一个子窗口
jmp GETNEXT_DLGITEM
.endif
dec ecx
inc edx
.endw
invoke SendMessage,@hWndSend,BM_CLICK,0,0 ;找到就模拟鼠标点击动作,发送完成
.break ;本窗口处理完成,准备去找下一个QQ窗口
GETNEXT_DLGITEM:
.endw
.endif
FINDNEXTWND:
mov eax,TRUE ;告诉 EnumWindows 函数,我要继续找下一个窗口,挨个找,一个都不能少^O^
ret
_EnumWindowsProc endp
;*********************************************************************************
;响应 WM_TIMER 消息,把列表中的内容按照某种顺序自动发送到QQ窗口
;*********************************************************************************
_Timer proc timer:DWORD
LOCAL @hWndCB:HWND
LOCAL @Count :DWORD
.if timer==ID_TIMER
invoke _SendMsg ;发送当前内容,然后预设下一次要发的内容
invoke GetDlgItem,hWndDlg,IDC_FILEMSG
mov @hWndCB,eax
invoke SendMessage,eax, CB_GETCOUNT, 0, 0
mov @Count,eax
invoke IsDlgButtonChecked,hWndDlg,IDC_RBN2
.if eax==TRUE ;循环发送方式
invoke SendMessage,@hWndCB,CB_GETCURSEL,0,0
inc eax
.if eax>=@Count
mov eax,1
.endif
.else
invoke IsDlgButtonChecked,hWndDlg,IDC_RBN3
.if eax==TRUE ;随机发送方式
dec @Count
invoke _rand,@Count
inc eax
.else
jmp SETSELMSG ;固定发送方式,就不用选了,但文本框中已清空,得去补上
.endif
.endif
invoke SendMessage,@hWndCB, CB_SETCURSEL,eax, 0 ;选中下一个要发的条目
SETSELMSG:
invoke _OnCBSelChange ;把选中项填入文本框中
.endif
ret
_Timer endp
;**************************************************************************************
;响应 IDC_AUTOSEND 按钮点击消息,设置时钟开关,按要求设置时钟,如果已经设过,则停止时钟
;**************************************************************************************
_AutoSend proc
LOCAL @hWnd:HWND
invoke GetDlgItem,hWndDlg,IDC_TIME
mov @hWnd,eax
invoke IsDlgButtonChecked,hWndDlg,IDC_AUTOSEND
xor eax,1 ;取反
push eax
invoke EnableWindow,@hWnd,eax ;如果是设时钟,则不让改时间,否则恢复为可改写状态
pop eax
.if eax==FALSE ;意思是:点击之前没选中,说明没有启动时钟
invoke GetDlgItemInt,hWndDlg,IDC_TIME,NULL,TRUE
.if eax<=0 ;时间不能少于1
invoke SetDlgItemInt,hWndDlg,IDC_TIME,6,TRUE ;太小则设为默认值
mov eax,6
.endif
mov edx,1000 ;换算为秒
mul edx
invoke SetTimer,hWndDlg,ID_TIMER,eax,NULL ;启动时钟
.else
invoke KillTimer,hWndDlg,ID_TIMER ;关闭时钟
.endif
ret
_AutoSend endp
;*********************************************************************************
;响应 CBN_SELCHANGE 消息,也就是手动改变列表选项时,把选中项的内容送入文本框中
;*********************************************************************************
_OnCBSelChange proc
LOCAL @hWnd:HWND
LOCAL @nSel:DWORD
LOCAL @hMem:HANDLE
LOCAL @pMem:HANDLE
invoke GetDlgItem,hWndDlg,IDC_SAVE
invoke EnableWindow,eax,FALSE ;让保存按钮变灰
invoke GetDlgItem,hWndDlg,IDC_FILEMSG
mov @hWnd,eax
invoke SendMessage,eax,CB_GETCURSEL,0,0
mov @nSel,eax
invoke SendMessage,@hWnd,CB_GETLBTEXTLEN,eax,0
push eax
inc eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,eax
mov @hMem,eax
invoke GlobalLock,eax
mov @pMem,eax
invoke SendMessage,@hWnd,CB_GETLBTEXT,@nSel,eax ;取出
invoke SetDlgItemText,hWndDlg,IDC_MESSAGE,@pMem ;填入
invoke GlobalUnlock,@pMem
invoke GlobalFree,@hMem
pop eax
ret
_OnCBSelChange endp
;*********************************************************************************
;根据给定的QQ号,打开临时聊天对话框
;*********************************************************************************
_OpenQQWnd proc
LOCAL posBuffer[255]:byte
invoke GetDlgItemText,hWndDlg,IDC_QQNUM ,addr lpBuf,20 ;取用户输入到文本
invoke wsprintf,addr posBuffer,addr lptemp,addr lpBuf ;连接文本串
invoke ShellExecute,NULL,NULL,addr posBuffer,NULL,NULL,SW_HIDE ;执行IE命令
ret
_OpenQQWnd endp
;*********************************************************************************
;窗口主调函数,判断并处理各种消息
;*********************************************************************************
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
mov eax,uMsg
.if eax==WM_INITDIALOG
mov eax,hWin
mov hWndDlg,eax
call _initDlg
.elseif eax==WM_COMMAND
mov eax,wParam
.if lParam!=0
mov edx,eax
shr edx,16
.if dx==BN_CLICKED ;按钮消息处理
.if ax==IDC_SEND ;发送
call _SendMsg
.elseif ax==IDC_SAVE ;保存
call _SaveMsg
.elseif ax==IDC_AUTOSEND ;自动项开关
call _AutoSend
.elseif ax==IDC_OPENQQWND ;打开临时QQ聊天对话框
call _OpenQQWnd
.endif
.elseif dx==EN_CHANGE ;文本框内容改变
.if ax==IDC_MESSAGE
invoke GetDlgItem,hWndDlg,IDC_SAVE
invoke EnableWindow,eax,TRUE
.endif
.elseif dx==CBN_SELCHANGE ;手动改变列表选项
call _OnCBSelChange
.endif
.endif
.elseif eax==WM_CLOSE
invoke KillTimer,hWndDlg,ID_TIMER
invoke EndDialog,hWin,0
.elseif eax==WM_TIMER ;时钟消息
invoke _Timer,wParam
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××
感谢4stOne的指正,我改过的内容放在24楼了,有兴趣的给测试一下。
******************************************************************************************************************
新改过的群发器可以让你随便指定一个QQ号,然后打开临时聊天窗口,自动发消息给他,编译后的exe文件在27楼。
- 标 题: 把我的第一个汇编程序(QQ自动群发器)源码与和我一样初级的编程爱好者共享
- 作 者:王仁军
- 时 间:2007-04-04 11:34
- 链 接:http://bbs.pediy.com/showthread.php?t=42116