Introduction
This main point of this article is not about how to make cloud drive works on Windows 7.
This article will teach you how to see what is going on inside. And take advantage from it.
It is even better than relying
on sparse, outdated or non-existant documentation.
This article is not a step by step tutorial of every tools I use. This would be
useless, documentation can be found on their website, and when you understand what
you want to, the UI is self descriptive.
You also don't need to be knowledgeable about Azure. In fact the destination of
this article is not important. The trip is the big deal.
But here is my goal : I want to create a new drive backed by Azure on Windows 7.
The code can be found on
MSDN, so I tried to run it on my windows 7 machine.
ERROR_UNSUPPORTED_OS you say ?
A quick search on Google
tells me that I can't use a blob drive outside Azure...
What a shame... But this is not my last word ! Today it is decided ! Today I'm gonna
fight !
How to hack your way through it
Digging deep inside CloudDrive.Create
with ILSpy
led me to this method
in a dll called mswacdmi.dll.
This assembly is a C++/CLI one, some parts are native, other managed, and here
I'm stuck at an unmanaged one...
This dll is in x64... my free edition of IDA Pro does not support x64, ollydbg
also does not support it, so I can't dig deeper.
But precious information can come from other sources than code. Code reverse engineering
should be used at the last resort.
"When in doubt... run
ProcMon" the great
Mark Russinovich said... So
that's what I did to see what file and registry access was going on under
the hood.
Ok it seems to be looking for registry stuff.
Moreover, creating a new drive is a kernel stuff, so I suspect that somehow, this
mswacdmi.dll module should try to start a driver, or communicate
with it.
So I use API monitor to see filter WIN API
calls happening in my program.
I decide to trace all file operation related API because that's how userland communicate
with a driver (WriteFile, ReadFile, CreateFile, DeviceIOControl
) as
well as WIN
API relating to services like OpenSCManager
and OpenService
.
(for those who do not
know,
drivers are started by the Service Control Manager)
I run again my code, and here is the result.
Ok, so the CloudDrive
service is missing on my computer.
To know more about this service, I create and deploy a new Azure role with remote
desktop enabled.
I connect to the role.
Then a quick sc qc CloudDrive show that the service exists in Azure,
but it is not a driver
as I thought.
(The type would be KERNEL_DRIVER
)
I also dump the missing registry that procmon told be earlier.
This remind me that Microsoft permit you to create a
VM Role
from an image created by yourself, so you can scale out any server or service you
want.
A quick search on internet inform me that such image should have
VM role integration components installed, so instances will be able to communicate
with Azure.
This installer is in the Azure SDK. "C:\Program Files\Microsoft SDKs\Windows
Azure\.NET SDK\2012-06\iso\wavmroleic.iso".
So I start a new Azure VM, and install it. (A
Azure VM is different from the VM role I did previously ! An Azure VM is
a plain old VM as we know it, the VM is never trashed
unlike instances in VM roles, but it can't scale.)
Unfortunately, my VM never reboot !
Frustrated, I set up a new VM hosted in my own server with Hyper-V and install
azure integration
components.
Then I check that the service is installed.
I check the registry, and take a dump of the registry that was missing.
I run my program aaand... it works !! I create a new drive backed by my blob storage.
Now I can use normal System.IO
API or use explorer
to write on the cloud !!
All is good, but I was not satisfied with the result.
Now I want to install it on my windows 7. Moreover a quick look
at the msi with InstEd,
show me all the stuff it installed on my VM... most of this stuff is not
related
with the azure drive feature, and is just azure infrastructure. This extra
stuff is maybe the
reason why my Azure VM never reboot. (Probably because the Azure Integration
Components should only be used by VM roles,
and not by Azure VM)
The solution is to remove all features not related with Azure Cloud
Drive from the install. For that, msi files have native support
for
transform files. A transform
file will apply a transformation on the msi file in memory just
before starting the installation.
InstEd makes it dead easy to create one.
The Features tab show you what the Features WADrive
will install. And surely enough,
you can recognize every single components.
CloudDriveSvc.exe, the userland part of the Cloud drive feature.
WADrive_wadrive.sys
the kernel part of the feature.
mswacdmi.dll the C/C++ interface in user code that interact with
this driver.
CloudDrive and StorageClient assemblies that are the high level
.NET API to manipulate CloudDrive
.
Just right click and delete the two other features.
To install this stuff on windows 7 you need to remove launch conditions in the
LaunchCondition table.
Save your transformation. (mst file)
Then run msiexec and pass the msi and the transform file.
Cool !!!
Happy from myself, I thought the battle was over, but I was wrong that's only the
beginning !!
I run the install on my Windows 7 but my CSharp program is just hanging.
A quick check with sc query show that the CloudDrive service is
not started and returned an
error.
I spy my application with API monitor. It seems that it is trying to communicate
with a named pipe that does not exist.
A quick handle search with procexp on my VM Windows server 2008 R2, show me that
this named pipe is opened, and probably created by CloudDrive.
In other words, on my windows 7 box, CloudDriveSvc.exe, the user
land part of cloud
drive, can't start. But the .NET API of CloudDrive
communicates
directly with it...
If the kernel part is not running, things will get hard because I will need
to use
windbg on a kernel driver, and that would be a different
story... In kernel mode,
you can forget all the nice tool we used to spy. Your hands would become
really
dirty.
So I used Driver Loader
from OSR to see if the driver is running. (I could also use sc query)
Good news, the driver is running on my windows 7 box.
Just to be sure I did not stripped too much stuff from the MSI, I run a new VM,
and create a transform without deleting features, but I remove launch conditions...
The result is the same.
Ok so now, let's dive in CloudDriveSvc.exe to see why it can't
start on win7.
To see where thing break, I will start CloudDriveSvc.exe (with
sc start CloudDrive) on Windows 2008 and Windows 7
and then compare the procmon traces.
So I stop CloudService on windows 2008, run procmon on both machine,
sc start CloudDrive,
and save the traces.
I save in both, PML file (native process monitor file, to open in in another instance
of procmon later), and in XML file. (Maybe I will need to run some code on the trace
to analyze more complicated stuff)
Here is the Windows 7 trace, and here the Windows 2008 R2 one.
Then I open side by side both traces, and try to sync them to see where the execution
path diverges.
And then I find the first divergence... (Left is Windows 2008, right is Windows
7)
What is this key ? I go to the registry and see that it is a COM component, and
the implementation is in vss_ps.dll . A quick check tell me that
it is the
Volume
Shadow Copy Service.
It allows you to backup files even if they are locked by other processes.
Now I know who is the enemy, I run API Monitor, filter call
for this COM component, and start spying CloudDriveSvc.
You can ask API Monitor to prevent new programs from starting so
you have time
to attach to it before it closes. That's what I did.
Here is the result on win7 box... this call is failing.
On the windows 2008 box.
And here are all the parameters, same on both machine.
A search on the E_INVALIDARG error on
MSDN
tells me :
Ok that explained the launch condition in the MSI... " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />
But the truth is that I don't care about the volume shadow copy feature... so,
if somehow I can modify the parameter VSS_PROV_HARDWARE (0x3)
to be VSS_PROV_SOFTWARE
(0x2)
. It should pass this call.
With the stack trace, I can see that RegisterProvider
is called directly
by
CloudDriveSvc.
And I know that the call will return to the RVA 0xE3BC.
(offset column)
If I open a disassembler a little bit before this address, I should see instructions
that push parameters on the stack, and a call x64 instruction.
Can I modify CloudServiceSvc.exe and change parameters ? Definitively.
So I run the great CFF explorer
! (a small dll file viewer, disassembler and hex
editor)
The problem is that what is called offset in API monitor
is in reality a
RVA, ie, the Relative Virtual Memory
address relative to the
base address of the module that own the function.
On the other hand, for CFF explorer, an offset
is a
file offset, ie the position
of a byte or instruction when it sit on the disk.
A dll is divided in multiple section. The position of these section is different
in virtual memory (Virtual Address, or RVA) than
on the
disk drive (Raw Address, or file offset).
The code section is .text (it is a convention). Moreover, you can
see that the
RVA of this section is 0x1000, and its
virtual size
25F06.
The return RVA of RegisterProvider
that I got earlier with
API Monitor
is E3BC, so, as you can see, it sits in the .text
section.
Here is how CloudDriveSvc.exe is mapped in the process's
virtual memory.
Here is the math to convert an RVA to a file offset.
E3BC (RVA) - 1000 (RVA of .text section) = D3BC (Relative address to the .text
section)
D3BC + 400 (File offset of .text section) = D7BC (File Offset)
So in other word, here is CloudDriveSvc.exe when it is sitting
on the drive.
I jump to the file offset with the disassembler from CFF explorer,
and move a little
bit before to see how the parameters are stacked.
I stop at D7AB. (Do not pay attention to "Base Address"
textbox and to
the Address column, whatever you choose here will not change the result,
it is
just a visualization feature)
You can see the value 0x3 is moved into the stack. So I just need
to change this
0x3 (VSS_PROV_HARDWARE
) with 0x2
(VSS_PROV_SOFTWARE
).
The address of 0x03 is D7AB + 4 = D7AF
Save your modification, and overwrite CloudDriveSvc.exe.
Restart the service.
If you spy CloudDriveSvc.exe, you can see that it effectively use
VSS_PROV_SOFTWARE
now.
I run again my program.
I wait, very anxious... and 1 min later, my program close without any crash and
I have
my new drive backed by a Azure VHD !!! " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />
Conclusion
Finally, you can support azure drive in windows 7 just by changing
just one bit in CloudDriveSvc.exe.
It reminds me a tale I read somewhere :
A company asks for a contractor to fix
this important mainframe that stopped to work.
The contractor comes, opens
the mainframe,
scratches his head, changes one screw, closes the mainframe, then the mainframe
comes
back from the dead.
-Ok, it cost you 10 000$.
-Isn't it expensive just for one screw ? Can you give me the detail of your invoice
?
-Sure, the screw costs : 1$, transport fees : 10$... and the knowledge for which
screw to change : 9989$
The result is cool but less interesting than the trip. (Truth to be told, I removed
the Azure drive once I knew it worked)
I hope you enjoyed it as well. But most importantly, be very grateful to the creators
of these tools. Without them, such thing would be impossible.
Thanks