-2

I want to modify Assembly.Location property at runtime in my application. I'm loading .NET Assemblies from the resources, and the loaded Assemblies (loaded from the memory) has this field empty and it breaks down application functionality.

I have no control over their source code. I know how to work around this bug in my own code, but that's not the case. And when they use Assembly.Location and gets an empty string the problem starts.

http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location.aspx

It's read only. Is there any low level way to do it? Any ideas?

My application is a loader for those applications embedded in the resources, so it doesn't depend on them. Try to load any assembly from the memory and check the Assembly.Location field of those loaded assemblies, it will be blank. They don't have the location because they're loaded from the memory, still I want to change that, either by .NET internals modifications or any other method.

The compressed assemblies cannot be decompressed to the disk. I don't mind the danger of major problems if you just know how to achieve it.

JDB
  • 25,172
  • 5
  • 72
  • 123
Bartosz Wójcik
  • 1,079
  • 2
  • 13
  • 31
  • 4
    *Why* are you trying to do this? Please give us more context - there may well be a better approach. – Jon Skeet Jun 24 '13 at 14:03
  • I'm loading .NET Assemblies from the resources, and the loaded Assemblies (loaded from the memory) has this field empty and it breaks down application functionality. – Bartosz Wójcik Jun 24 '13 at 14:04
  • 1
    Well *what* functionality does it break? Are these assemblies you control? If so, you should look at whether you can fix them to not rely on this property. – Jon Skeet Jun 24 '13 at 14:07
  • 1
    I have no control over their source code, that's the problem! I know how to work around this bug in my own code, but that's not the case. And when they use Assembly.Location and gets an empty string the problem starts ;) – Bartosz Wójcik Jun 24 '13 at 14:08
  • 5
    All of this is context which should have been in your question. – Jon Skeet Jun 24 '13 at 14:13
  • 1
    The question doesn't really make sense, your application *depends* on the location of the assemblies, but the assemblies are embedded so don't have a location on disk. – James Jun 24 '13 at 14:16
  • My application is a *loader* for those applications embedded in the resources, so it doesn't depend on them. Try to load *any* assembly from the memory and check the Assembly.Location field of those loaded assemblies, it will be blank. – Bartosz Wójcik Jun 24 '13 at 14:18
  • @BartoszWójcik It doesn't depend on the assemblies themselves, but it clearly depends on the *location* of where those assemblies are supposed to be - which is why it doesn't make sense because they will never have a location. Maybe I am misinterpreting your question but I don't full understand the *why* behind what you are doing. – James Jun 24 '13 at 14:28
  • @James Yes, they don't have the location because they're loaded from the memory, still I want to change that, either by .NET internals modifications or any other method. – Bartosz Wójcik Jun 24 '13 at 14:32
  • @BartoszWójcik again the question begs - why? If you just need to map a directory to an assembly why not just manage it from inside your application? You could maintain a mapping of assembly name/path. – James Jun 24 '13 at 14:37
  • Because I want it so, again, if you don't know the solution, please do not try to convince me to not to do it. What's the point of being a programmer, if you can't solve difficult issues? – Bartosz Wójcik Jun 24 '13 at 14:40
  • I'm not familiar with compressed assemblies loaded from resources, so this idea may well be a non-starter: Are you perhaps able to feed the assembly into ILDASM, and round-trip it through ILASM after modifying the intermediate text? I know this sounds far out, but this is actually one recommended way of changing the strong name signing of an assembly. – RenniePet Jun 24 '13 at 15:09
  • The same question without the right answer http://stackoverflow.com/questions/16597138/loading-an-assembly-by-bytes-loses-the-location – Bartosz Wójcik Jun 24 '13 at 16:17
  • 8
    I disagree with the close votes. This question is not too localized... it was presently in a general context and addresses a real concern for library developers, one which is not well documented. I believe this question has merit. – JDB Jun 24 '13 at 18:00
  • I know I am taking a risky path here.. but is the access to Assembly.Location inside another managed assembly? If so, you could think about rewriting the _client_ code and hijack the call to your implementation, which will return whatever you like. Could it be a feasible approach? – Lorenzo Dematté Jun 26 '13 at 14:40
  • I am not saying it _should_ be done.. but could it be a way? (oh, and I see @RenniePet suggested more or less the same think.. I was thinking more of an in-memory approach, but still...) – Lorenzo Dematté Jun 26 '13 at 14:41
  • 5
    Given that this was linked to from the 'war of the closes' blog post, and there is a link to a +11 question that is asking essentially the same thing, it looks like there as an aspect of 'downvote due to personality' going on here. – tacaswell Jun 26 '13 at 16:55
  • @tcaswell - I can't find a link to this question anywhere on that blog post. – JDB Jul 05 '13 at 01:49

4 Answers4

8

Copy the desired assembly resource out of its container assembly into the container assembly's directory or into a subdirectory. Then load the copied assembly into your application. This would be significantly easier then trying to change a private field (or the result of a function call).

The Assembly class exposes a public interface - they way it's supposed to be used. Attempting to modify the internal working of the class could cause you major problems, assuming it's even possible. Some future version, or even just a regular update, could change the internal workings of the class and break your code. You also cannot predict what other parts of the class are dependent on that field. Changing its value could have unintended consequences futher on. You are proposing changing the class from its defined behavior which could cause other assemblies or programmers further down the road confusion and frustration. That's why this field was implemented as read-only and that's why .NET provides no easy way to modify read-only values.


UPDATE

Here's the source code for the Location property:

[System.Security.SecurityCritical]  // auto-generated
[ResourceExposure(ResourceScope.Machine)] 
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity] 
private static extern void GetLocation(RuntimeAssembly assembly,
    StringHandleOnStack retString); 

public override String Location 
{
    [System.Security.SecuritySafeCritical]  // auto-generated
    [ResourceExposure(ResourceScope.Machine)]
    [ResourceConsumption(ResourceScope.Machine)] 
    get {
        String location = null; 

        GetLocation(GetNativeHandle(), 
                    JitHelpers.GetStringHandleOnStack(ref location));

        if (location != null)
            new FileIOPermission( FileIOPermissionAccess.PathDiscovery, location ).Demand();

        return location; 
    }
} 

Note that this code is actually located inside an undocumented class named RuntimeAssembly which is defined as internal within the Assembly class. You can see the full source code here:
http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Reflection/Assembly@cs/1305376/Assembly@cs

As you can see, there is no backing field here to modify. There is no way to override the Location property as you desire (without rewriting pieces of the Windows OS).

AND... just in case you get a hankerin' for rewriting that GetLocation function, you may be interested in this Q/A:
What is [DllImport("QCall")]?
(It probably goes without saying that, at this point, you are on your own.)

Community
  • 1
  • 1
JDB
  • 25,172
  • 5
  • 72
  • 123
  • I'm sorry, but the compressed assemblies cannot be decompressed to the disk. I *want* major problems if you just know how to achieve it. – Bartosz Wójcik Jun 24 '13 at 15:01
  • 8
    This is the first time you've mentioned *compression*. I think it would help if you could explain what your are doing and why you are doing it. You said *"What's the point of being a programmer, if you can't solve difficult issues?"*. Part of the job is *finding another way* when the current way won't get you where you want to go. See [How to Ask a Question](http://stackoverflow.com/help/how-to-ask): *"The answer to your question may not always be the one you wanted, but that doesn’t mean it is wrong."* – JDB Jun 24 '13 at 15:04
  • 3
    (By the way, [Jon Skeet](http://stackoverflow.com/users/22656/jon-skeet) is one of the foremost experts on C#. If he says it can't be done, or shouldn't be done, it would be wise to give considerable attention to his advice.) – JDB Jun 24 '13 at 15:11
  • I don't care if he's an expert. I just want the job to be done and if you can't do it, it doesn't mean it can't be done. It's just a code loaded in the memory, so it CAN be done. I'm going to start a bounty on this one in two days. – Bartosz Wójcik Jun 24 '13 at 15:42
  • I've seen implementation of GetLocation in Reflector (that was the first place I put my eyes on), it leads to CLR.dll native code, I'm thinking about putting native hook in this lib. – Bartosz Wójcik Jun 24 '13 at 19:12
  • 2
    @Bartosz - Would this have anything to do with [netshrink](http://www.pelock.com/products/netshrink)? Your question, at its core, was a good one. I think that if you had explained things better, it might have actually gotten some good attention and solid answers. Perhaps if you care to revisit it, it can be reopened. – JDB Jun 24 '13 at 20:51
  • +1 For your research. Just wondering, that link you provide to dotnetframework.org - it doesn't work very well in either Firefox or IE 10. And where did they get that source code, has Microsoft made it public? As for your guess about netshrink, Bartosz' SO profile includes a link to that web site, so that is presumably what it's all about. – RenniePet Jun 24 '13 at 21:38
  • @RenniePet - I got the netshrink link from Bartosz's profile. Microsoft released the source code for the .net framework under their reference license. The website isn't very good, but it's easier than downloading and installing the full source code. – JDB Jun 24 '13 at 23:04
  • 9
    @Cyborgx37 fair play for trying to see this issue to a close, but unfortunately the OP appears very stubborn and won't accept that what he is trying to do isn't really achievable using any standard/acceptable approach (if any). Part of being a *good* developer is knowing when you are taking the wrong approach, understanding why, and seeking an alternative - the OP seems unwilling to do that so IMO he may as well be left to his own devices. – James Jun 25 '13 at 12:28
  • @RenniePet: The source code is public: http://referencesource.microsoft.com/netframework.aspx – jgauffin Jul 05 '13 at 13:47
4

Simply put, you can't modify the property. In this case it is informing you where that Assembly was loaded from disk. Instead you would need to move the assembly and ensure your application loads it from the new location.

Ian
  • 33,605
  • 26
  • 118
  • 198
  • That's not the answer, I just want to modify this property either by reflection, runtime bytecode modification or any other means necessary. – Bartosz Wójcik Jun 24 '13 at 14:06
  • 7
    @BartoszWójcik: No, that really *is* the answer. If you're using assemblies which rely on Assembly.Location being a value which you can't achieve, then those assemblies aren't fit for the purpose you're using them for. – Jon Skeet Jun 24 '13 at 14:14
  • Still it's no valid answer. .NET is a framework and it must be a way to modify it's internal variables and read only fields, I'm not looking for any *documented* way. – Bartosz Wójcik Jun 24 '13 at 14:17
  • 2
    @BartoszWójcik: You may think you want to modify the field via Reflection but that's going to cause you more pain later down the line. You'll start piling workaround onto workaround. Let's try and fix the initial issue instead – Ian Jun 24 '13 at 14:17
  • But that's the initial problem. Two assemblies are binded together under the same disk path. One of them is loaded from the memory and I want to set the location field to point to the executable it's stored within. If you don't know the solution, please allow others to comment. – Bartosz Wójcik Jun 24 '13 at 14:24
  • 2
    @BartoszWójcik: Again it's the wrong thing to do, you can't even set the field via Reflection because it has no internally stored variable. You'd instead need to change the result of an internal function which calls out to the Windows API. – Ian Jun 24 '13 at 14:32
  • My friend show me this blog entry http://blogs.msdn.com/b/thottams/archive/2006/04/26/583722.aspx and told me it might be possible to modify the getter method of Assembly.Location to return whatever I want, need to try this one. – Bartosz Wójcik Jun 24 '13 at 15:43
  • Look I don't know what you know about exe-compression, but it's the issue and it has to be resolved. You might not like it, but not all problems are nice and easy to fix. You might argue with it, but it's the real problem for my customers and I have to deal with it. You told me about the solution but didn't show any code, I guess I'm not the only bad programmer here... – Bartosz Wójcik Jun 24 '13 at 16:03
4

Although @Cyborgx37 is correct - you cannot change the Location of existing assembly, there is other way to approach this problem.

Instead of trying to set the location of already loaded assembly, use library like Mono.Cecil to rewrite assemblies using this property.

Assuming it is not one of core .NET framework assemblies, it is pretty easy to replace all Assembly.Location accesses with your own static property which you can inject.

I had to do things like that a lot when writing my own encrypted assembly loader, and was quite successfull.

EDIT

If you are really dead set on doing this, those two articles should get you started on how to rewrite exising method at runtime. There are multiple caveats though: This does not work with NGEN or GAC at all, and it will have multiple hard issues with async/await/enumerable/exception handling which you will have to solve. Use at your own risk.

MSIL Injection: Rewrite a non dynamic method at runtime

CLR Injection: Runtime Method Replacer

I would strongly advice against it, as those solution are far too brittle to use in production code, which was mentioned by authors:

We need to keep in mind that we are directly manipulating the CLR memory in ways not intended. This code might not work with newer versions of the .NET framework. This was tested with .NET 3.5 on a Vista x86, and might not work on your machine.

and

Microsoft does not support any of this hacky stuff. You should look at another means to accomplish your goal if possible.

ghord
  • 13,260
  • 6
  • 44
  • 69
  • That's cool! It would seem to fit with the OP's goals. I would have accepted this answer. – JDB Jul 05 '13 at 01:16
1

Slightly longer answer;

It would be somewhat difficult (as I am sure you're already suspecting), the reason for it being hard is that the value returned by the Location getter isn't stored in a backing field such as

  private readonly string location;

Instead, it is fetched using the native handle to the Assembly (or actually since that class is abstract, RuntimeAssembly which is the sub-class you get when loading stuff from memory) and then using an external dll call to read the location.

It looks something like this (simplified);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetLocation(RuntimeAssembly assembly, StringHandleOnStack retString);

public override String Location {
  get {
    String location = null;
    GetLocation(GetNativeHandle(), JitHelpers.GetStringHandleOnStack(ref location));

    return location;
  }
}

It is going to be easier for you to change the code that requires this to be non-empty than inject a value into that.

--L Lawliet--

ggcodes
  • 2,859
  • 6
  • 21
  • 34