What's wrong with this dll ?
It may be OK, but if the object.Value(Set) statements are structured in a certain way, the output(Get) produces string errors.
Here is the main:
;-------------------------------------------------------------------------------
.code
DBGWIN_DEBUG_ON = 1 ;turn it off if you don't want to include debug info into the program
DBGWIN_EXT_INFO = 1 ;turn it off if you don't want to include extra debug info into the program
;-------------------------------------------------------------------------------
CreateMyCom3 PROC this_:DWORD
LOCAL hMem:DWORD
nop
nop
nop
pObjectData this_, edx ; cast this_ to object data
push edx
nop
nop
nop
pop edx
mov DWORD ptr DS:(MyCom3ObjData ptr [edx]).m_Str, 00650065h
nop
nop
nop
xor eax, eax ; return S_OK
nop
nop
nop
xor eax, eax ; return S_OK
ret
CreateMyCom3 ENDP
;-------------------------------------------------------------------------------
GetValue PROC this_:DWORD, pStr:DWORD ; GetValue for the IMyCom Interface
nop
nop
db 0090h
nop
pObjectData this_, edx ; cast this_ to object data
push ebx
push ebp ; has to be done before changing ESP
push eax
mov eax, (MyCom3ObjData ptr [edx]).m_Str ; get object data value
mov ecx, pStr ; get ptr to variable for return
mov [ecx], eax
push ecx
push eax
sub eax, 4
mov ecx, eax
mov eax,ecx
add eax,00h
pop eax
pop ecx
pop eax
xor eax, eax ; return S_OK
nop
nop
nop
ret
GetValue ENDP
;-------------------------------------------------------------------------------
SetValue PROC this_:DWORD, newStr:DWORD ; SetValue for the IMyCom Interface
nop
nop
nop
pObjectData this_, edx ; cast this_ to object data
push edx
push ebp
push esp
nop
nop
nop
push esi
push edi
mov eax, newStr ; get variable
mov (MyCom3ObjData ptr [edx]).m_Str, eax ; store new value
nop
nop
nop
pop edi
pop esi
pop esp
pop ebp
pop edx
xor eax, eax
ret
SetValue ENDP
;
Thank you for reading.
Well apart from all the bizarre NOPs and the pushing and popping of EBP/ESP which almost certainly isn't doing what you want it to (since it looks suspiciously like you're trying to create a stack frame manually which MASM does for you anyway by default), the biggest ringer is this line, which does nothing:
pObjectData this_, edx ; cast this_ to object data
There's no such thing as a "cast" in MASM, that's C terminology, a DWORD in a register can be anything you want it to be as long as it is consistent, the assembler doesn't care where it's come from or whether it's data or a pointer. In this line there's no assembler operation anyway. What you might mean is the MASM ASSUME syntax, which tells the assembler to treat a register as a pointer to a structure:
ASSUME EDX:pObjectData
assuming that pObjectData has previously been defined correctly as a structure in the .DATA section. You didn't actually move the "this_" value into the EDX register yet, though! That would then allow you to use the dotted sub-element notation which you use later, but also incorrectly:
mov eax, MyCom3ObjData[edx].m_Str ; get object data value
assuming that m_Str is an element of the pObjectData structure and MyCom3ObjData is a defined memory location containing an array of such structures. Using the PTR syntax in the middle is incorrect. For that line to be meaningful EDX would have to be a multiple of the SIZEOF the structure, so you'd be pointing to a specific pObjectData structure in the array. If the EDX value is a pointer direct to your structure, then this line won't work. When you have finished with using EDX in this way, you must release the assumption with:
ASSUME EDX:NOTHING
otherwise following code will not assemble correctly. I'll leave it to others to pick apart the stack handling.
Ian_B
So far, I have based it on the com\example MyCom's.
Thanks.
Also it does not conform to the structure of a DLL.
Casper
Anyone's insight into Casper's structure statement ?
Where's Ernest anyway ? He started it !
Quote from: Casper on June 17, 2006, 10:44:08 AM
Also it does not conform to the structure of a DLL.
This is true, but I'd have thought that was a problem to be solved once pseudocode had been replaced with actual workable code.
Askm, the info you need on constructing a DLL is here: http://website.masm32.com/dlltute/masmdll.htm - but you'll need to do a lot more work on the code before you get to that stage.
Some of you may know the answer.
May it be in the stacks ?
Or cannot a SetValue reference itself ?
Okay a DLL MUST start like this...
DllEntry proc hInst:DWORD, reason:DWORD, reserved1:DWORD
;---------------------------------------;
.if reason==DLL_PROCESS_ATTACH ; When the dll is loaded
push hInst
pop hInstance
call Main
.endif
mov eax,TRUE
ret
;---------------------------------------
DllEntry Endp
You can rename Main to match your procedure. A DLL must ALWAYS return True.
The two above steps are missing in your code.
At the end, you MUST put ...
End DllEntry
Casper
He wouldn't necessarily have to call 'Main' in his entry proceedure or any other funtion for that matter would he?
Could'nt he just call the exported functions as needed?
Rags
rags,
No, only if he wants the DLL to execute as soon as it is loaded by an application. Otherwise there should be no call. Thank you for reminding me that there is more than one type of DLL.
Casper
Something sounds wrong with this description, you can load a DLL in a couple of ways, statically when the app starts or dynamically with LoadLibrary(), GetProcAddress() .... FreeLibrary() after but in either of these techniques you then must call the procedure you require. The DLLmain/LibMain or whatever you call the entry point proc is caled only by the OS, not the calling app. You uase it to set up any conditions you require before you call a procedure in the DLL.
Get & Set are COM callbacks, which are normally in a ocx ( a dll by another name ).
askm,
Begin by going through COM tutorial and examples here. You are way behind the learning curve and you need to catch up. Also check out the "Custom Interface Components" here as well.
Learn Well, then Do Well !!! :U
Regards, P1 :8)
Hutch,
Please do not be offended. I think you need to read up on CPL files. They are actually DLLs. This is from one that I have in my archive.
include Protect.inc
include Macros.asm
include registry.asm
include Dialogs.asm
CPlApplet PROTO :DWORD, :DWORD, :DWORD, :DWORD
.CODE
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
;--------------------------------------
.IF reason==DLL_PROCESS_ATTACH
INVOKE GetModuleHandle, addr nameCPL ; NULL
mov hInstance, eax
.ENDIF
mov eax,TRUE
ret
;--------------------------------------
DllEntry Endp
CPlApplet proc hWnd:DWORD, uMsg:DWORD, lParam1:DWORD, lParam2:DWORD
;--------------------------------------
.IF uMsg == CPL_INIT
.ELSEIF uMsg == CPL_GETCOUNT
mov eax, 1
.ELSEIF uMsg == CPL_INQUIRE
push esi
mov esi, lParam2
assume esi:ptr CPLINFO
mov [esi].lData, 0
mov [esi].idIcon, 200
mov [esi].idName, 3000
mov [esi].idInfo, 3001
assume esi: nothing
pop esi
.ELSEIF uMsg == CPL_DBLCLK
; Set up the 1st property sheet
mov PropSheet.dwSize, sizeof PROPSHEETPAGE
m2m PropSheet.dwFlags, PSP_DEFAULT + PSP_USEICONID
m2m PropSheet.hInstance, hInstance
m2m PropSheet.pszTemplate, offset mydb1
mov PropSheet.pszIcon, 201
m2m PropSheet.pfnDlgProc, offset DialogFunc1
m2m PropSheet.pszTitle, offset psztitle
mov PropSheet.lParam, 0
mov PropSheet.pfnCallback, 0
; Set up the 2nd property sheet
mov PropSheet[(sizeof PROPSHEETPAGE)].dwSize, sizeof PROPSHEETPAGE
m2m PropSheet[(sizeof PROPSHEETPAGE)].dwFlags, PSP_DEFAULT + PSP_USEICONID
m2m PropSheet[(sizeof PROPSHEETPAGE)].hInstance, hInstance
m2m PropSheet[(sizeof PROPSHEETPAGE)].pszTemplate, offset mydb2
mov PropSheet[(sizeof PROPSHEETPAGE)].pszIcon, 202
m2m PropSheet[(sizeof PROPSHEETPAGE)].pfnDlgProc, offset DialogFunc2
m2m PropSheet[(sizeof PROPSHEETPAGE)].pszTitle, offset psztitle
mov PropSheet[(sizeof PROPSHEETPAGE)].lParam, 0
mov PropSheet[(sizeof PROPSHEETPAGE)].pfnCallback, 0
; Set up the 3rd property sheet
mov PropSheet[(sizeof PROPSHEETPAGE)*2].dwSize, sizeof PROPSHEETPAGE
m2m PropSheet[(sizeof PROPSHEETPAGE)*2].dwFlags, PSP_DEFAULT + PSP_USEICONID
m2m PropSheet[(sizeof PROPSHEETPAGE)*2].hInstance, hInstance
m2m PropSheet[(sizeof PROPSHEETPAGE)*2].pszTemplate, offset mydb3
mov PropSheet[(sizeof PROPSHEETPAGE)*2].pszIcon, 203
m2m PropSheet[(sizeof PROPSHEETPAGE)*2].pfnDlgProc, offset DialogFunc3
m2m PropSheet[(sizeof PROPSHEETPAGE)*2].pszTitle, offset psztitle
mov PropSheet[(sizeof PROPSHEETPAGE)*2].lParam, 0
mov PropSheet[(sizeof PROPSHEETPAGE)*2].pfnCallback, 0
; Create the 3 Pages
invoke CreatePropertySheetPage, ADDR PropSheet
mov hPs, eax
invoke CreatePropertySheetPage, ADDR PropSheet[sizeof PROPSHEETPAGE]
mov hPs[4], eax
invoke CreatePropertySheetPage, ADDR PropSheet[(sizeof PROPSHEETPAGE)*2]
mov hPs[8], eax
; Set up the property sheet header
mov PropHdr.dwSize, sizeof PROPSHEETHEADERA
mov PropHdr.dwFlags, PSH_DEFAULT
m2m PropHdr.hwndParent,NULL ;hWnd
m2m PropHdr.hInstance, hInstance
mov PropHdr.pszIcon, 0
m2m PropHdr.pszCaption, offset caption
mov PropHdr.nPages, 3
mov PropHdr.nStartPage, 0
m2m PropHdr.phpage, offset hPs
mov PropHdr.pfnCallback, 0
; Display the property sheet control
invoke PropertySheet, ADDR PropHdr
.ELSEIF uMsg == CPL_STOP
return 0
.ELSEIF uMsg == CPL_EXIT
return 1
.ENDIF
ret
;--------------------------------------
CPlApplet endp
END DllEntry
Casper
Casper,
I think you'll find that the DllEntry procedure doesn't call any other procedure or do any 'real' work. CPL files have to be started from, IIRC, control.exe, which will call CPlApplet. DllEntry is called as part of the LoadLibrary (or whatever is used).
Cheers,
Zooba :U
zooba,
You have missed the entrire point of the discussion. The point Hutch is making is that there must be a caal statement in the DLL (a CPL file 'is' a DLL) as follows:
Quoteyou then must call the procedure you require
As you can see in my code, there is no call to CPlApplet, which is the 'workhorse' of this DLL. Don't worry about it, not many know about the CPL file and the fact that it is a DLL. By the way, you are right, there is a file association that calls the program that runs this type of DLL, it is RunDLL.exe (not control.exe, this program just opens a window and lists all the CPL files, nothing more).
Casper
Quote from: Casper on June 25, 2006, 01:12:23 PM
zooba,
You have missed the entrire point of the discussion.
Strange, I thought I had followed it perfectly.
Quote from: Casper on June 25, 2006, 01:12:23 PM
The point Hutch is making is that there must be a call statement in the DLL
Quote from: hutch-- on June 23, 2006, 12:53:50 PM
you can load a DLL in a couple of ways ... you then must call the procedure you require.
Nope, Hutch's point was definitely that you load it first and then call the procedure.
In fact, I'll almost go so far as to say you've missed the point:
Quote from: Casper on June 22, 2006, 03:33:02 PM
Okay a DLL MUST start like this...
DllEntry proc hInst:DWORD, reason:DWORD, reserved1:DWORD
;---------------------------------------;
.if reason==DLL_PROCESS_ATTACH ; When the dll is loaded
push hInst
pop hInstance
call Main
.endif
mov eax,TRUE
ret
;---------------------------------------
DllEntry Endp
The 'call Main' is the exact line that isn't required. There are few (if any) legitimate purposes for executing large amounts of code while attaching to a process. More likely you'd initialise the minimum number of variables required and export an initialisation function to allow the calling application to run it when it wants to.
This is exactly what rags pointed out to you, which you apparently ignored and then posted code proving yourself wrong. There is only one 'type' of DLL, but there are infinite amount of uses and a large number of standard interfaces (such as COM, ActiveX, control panels, Quick Editor plugins, etc.).
The defining characteristic of a dynamic-link-
library, is that it is useless on its own. Without a process to load it, call the functions exported from it, and close it, it can do nothing.
Casper,
The code you posted is a perfectly normal DLL, it has the entry point which is only called by the OS and in that proc it sets the instance handle. The following procedure for the Control Panel is not called by the Entry Point procedure but by another application. If you have a look in the masm32 example code there is a smal demo for calling control panel apps called RUNCPL that uses this code to start a CPL DLL.
STRING RunDLL,"rundll32.exe shell32.dll,Control_RunDLL "
mov BYTE PTR path[0], 0
invoke szMultiCat,2,ADDR path,ADDR RunDLL,ADDR buffer
invoke WinExec,ADDR path,SW_SHOW
Never liked WinExec, this is the RunCPL routine from WinExplorer...
RunCPL FRAME pszCPLFile
LOCAL CmdLine[MAX_PATH] :B
LOCAL sui :STARTUPINFO
LOCAL pi :PROCESS_INFORMATION
CInvoke(wsprintf,offset CmdLine,'rundll32 shell32,Control_RunDLL "%s"',[pszCPLFile])
mov D[sui.cb],SIZEOF STARTUPINFO
invoke GetStartupInfo,OFFSET sui
invoke CreateProcess,NULL,OFFSET CmdLine,NULL,NULL,\
TRUE,NULL,NULL,NULL,OFFSET sui,OFFSET pi
test eax,eax
jz >.ERROR
invoke CloseHandle,[pi.hProcess]
invoke CloseHandle,[pi.hThread]
xor eax,eax
RET
.ERROR
xor eax,eax
dec eax
ret
ENDF
Basically the same thing but using CreateProcess instead.
Donkey
Everybody,
Thank you for setting me straight. I struggle with English at times but I do okay. Donkey I never saw anything quite like that type of replacement for winexec.
Casper
Hi Casper,
You should never use WinExec, it is flawed and a security risk. If you mistakenly decide to use it make sure you always encorporate the path in quotes as specified by Microsoft on MSDN. CreateProcess is the only function you should be using to launch a process, WinExec is deprecated and has a security hole due to the command line parser it uses.
Quote from: MSDN WinExecSecurity Remarks
The executable name is treated as the first white space-delimited string in lpCmdLine. If the executable or path name has a space in it, there is a risk that a different executable could be run because of the way the function parses spaces. The following example is dangerous because the function will attempt to run "Program.exe", if it exists, instead of "MyApp.exe".
WinExec("C:\\Program Files\\MyApp", ...)
If a malicious user were to create an application called "Program.exe" on a system, any program that incorrectly calls WinExec using the Program Files directory will run this application instead of the intended application.
To avoid this problem, use CreateProcess rather than WinExec.
CreateProcess uses the same parser for the lpCommandLine argument if, as in your example above, lpApplicationName is blank (of course, your example doesn't give a full path so it's fine, but so is Hutch's):
Quote from: MSDN CreateProcessThe lpApplicationName parameter can be NULL. In that case, the module name must be the first white space-delimited token in the lpCommandLine string. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin; otherwise, the file name is ambiguous. For example, consider the string "c:\program files\sub dir\program name". This string can be interpreted in a number of ways. The system tries to interpret the possibilities in the following order:
c:\program.exe files\sub dir\program name
c:\program files\sub.exe dir\program name
c:\program files\sub dir\program.exe name
c:\program files\sub dir\program name.exe
Cheers,
Zooba :U
An interesting enough example, I can duplicate Edgar's example with WinExec so that it runs the wrong file but I can also duplicate it with CreateProcess() if the 2nd argument contains the file name instead of the first.
fn CreateProcess,"long name\call me.exe",NULL, \ ; works correctly
NULL,NULL,NULL,NULL,NULL,NULL, \
ADDR st_info, \
ADDR pr_info
fn CreateProcess,NULL,"long name\call me.exe", \ ; fails like WinExec
NULL,NULL,NULL,NULL,NULL,NULL, \
ADDR st_info, \
ADDR pr_info
Both WinExec() and CreateProcess() fail when they are passed a command line so I would tend to see the problem as an OS defect that needs to be fixed by the vendor and if you are worried about this potential problem, use one form of CreateProcess() where you seperate the file name from the command tail as a work around.
Edgar's suggestion fixes the problem in both CreateProcess() and WinExec().
main proc
LOCAL st_info:STARTUPINFO
LOCAL pr_info:PROCESS_INFORMATION
.data
fname db 34,"long name\call me.exe",34,0
.code
fn WinExec,OFFSET fname,1
; ---------------------
; zero fill STARTUPINFO
; ---------------------
mov ecx, 17 ; 68 bytes SIZEOF STARTUPINFO
lea edx, st_info
xor eax, eax
@@:
mov [edx], eax
add edx, 4
sub ecx, 1
jnz @B
mov st_info.cb, 68 ; set the structure size member
fn CreateProcess,NULL,OFFSET fname, \
NULL,NULL,NULL,NULL,NULL,NULL, \
ADDR st_info, ADDR pr_info
invoke CloseHandle, pr_info.hThread
invoke CloseHandle, pr_info.hProcess
ret
main endp
Here is a simple proc that should be as easy to use as WinExec().
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
AppExec proc pcline:DWORD
LOCAL pbuf :DWORD
LOCAL buffer[260]:BYTE
LOCAL st_info:STARTUPINFO
LOCAL pr_info:PROCESS_INFORMATION
mov pbuf, ptr$(buffer)
mov pbuf, cat$(pbuf,chr$(34),pcline,chr$(34))
; ---------------------
; zero fill STARTUPINFO
; ---------------------
mov ecx, 17 ; 68 bytes SIZEOF STARTUPINFO
lea edx, st_info
xor eax, eax
@@:
mov [edx], eax
add edx, 4
sub ecx, 1
jnz @B
mov st_info.cb, 68 ; set the structure size member
fn CreateProcess,NULL,pbuf,NULL,NULL,NULL,NULL,NULL,NULL, \
ADDR st_info, ADDR pr_info
invoke CloseHandle, pr_info.hThread
invoke CloseHandle, pr_info.hProcess
ret
AppExec endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
I imagine that's exactly what WinExec does now (if not forever) anyway. I don't see why they'd duplicate code.
The 'work-around' of passing the module name and the parameters separately is actively encouraged by Microsoft, and the potential problems with not doing so are well documented. If programmers choose to ignore it, it's their own fault.
Having said that, I think someone posted a while back that IE apparently doesn't separate the program for 'View Source' from the file... :bdg
Cheers,
Zooba :U
askm,
I am many sorry to cause this deviation but we both are learning something as a result, right.
Hutch,
I am adding the last version into a project so I will nmot loose it. This may be my learning showing but I have one more question and then I will shut up. pcline is a pointer to the program to run, I think. I think it could point to a string that might contain "c:\windows\notepad.exe" So I think I know that one but in the following:
mov pbuf, ptr$(buffer)
mov pbuf, cat$(pbuf,chr$(34),pcline,chr$(34))
What does buffer contain?
Casper
Casper,
The buffer is just a piece of memory to add the other strings to. The "ptr$" macro zeros it and the "cat$" macro appends string data to the blanked buffer one piece at a time.
Oh, Now I understand how it works. It is very nice and I thank you for it. Now, I will play.
Casper