[编程工具]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楼。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: