1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
//Copyright 2016 William Cody Laeder
//
//Licensed under the Apache License, Version 2.0 (the "License");
//You may not use this file execpt in compliance with the License.
//You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//Uless required by applicable law or agreed to in writing, software
//distributed under the License is distrubuted on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or
//implied. See the License for specific language governing permissions and
//limitations under the License.

//! DynLib
//!
//! Provides a relatively _safe_ interface to interact with windows dll
//! loading. This crate should only be used on windows platforms. If you
//! attempt to use it on non-windows platform you will likely get a
//! compiler error.
//!
//! The _entry point_ to this crate is `LoadWinDynLib`. This allows the 
//! developer to set configuration flags. There are some details contained
//! within the type page. Most specifcally one should check which flags
//! are and are not allowed. There is some compatibility issues between
//! flags, as they'll modify _where_ files are located.
//!
//! The loaded library does not have a built in drop flag. You are
//! responsible for dropping your loaded DLL's. Generally speaking
//! you have to be responsible for this. When the DLL is unloaded from
//! memory the symbols/functions you have loaded are invalidated,
//! attempts to call them will result in a memory fault, and likely
//! process termination.
//!
//! An example project to generate a win dll [is
//! here](https://github.com/valarauca/dynlib/examples)]. A few cargo
//! directives are required, as well you must ensure you are building with
//! the `rust-msvc` compiler.


extern crate winapi;
extern crate kernel32;

use std::ffi::OsString;
use winapi::minwindef::HMODULE;
use winapi::c_void;
use kernel32::{LoadLibraryExA,GetProcAddress, FreeLibrary};
use std::io::Error;
use std::mem;

///Untyped Pointer to a Function.
///
///An untyped pointer returned from an attempt to load a function.
///The developer will have to use `unsafe{ mem::transmute(VoidPtr)}`
///to recover the typing, and make this location executable.
pub type VoidPtr = *const c_void;


///Options for loading a Dynlib/Exe into windows.
///
///This structure acts like the `OpenOptions` structure in StdLib. 
///
///For full documentation see [The
///Docs](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx)
///A cursory explaination is provided before each function. Each function
///corresponds to one flag in the `LoadLibraryExA` interface. 
#[derive(Copy,Clone)]
pub struct LoadWinDynLib {
    x: u32,
}
impl LoadWinDynLib {

    ///Create a new options value
    pub fn new() -> Self {
        LoadWinDynLib {
            x: 0
        }
    }

    ///Ignore Code Auth Levels
    ///
    ///This value tells the loader not to check App Locker or Software
    ///Software Restriction Polices for the DLL/EXE you are loading.
    ///
    ///If that DLL/EXE's dependencies DO have Code Auth Level restrictions you
    ///may will encounter an error.
    pub fn ignore_code_authz_level<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000010u32;
        self
    }

    ///Library As Data File
    ///
    ///The library will be loaded into virtual memory. The memory will not be
    ///flagged as executable, so calling functions from the library will
    ///result in errors.
    ///
    ///This is used in conjuction with other flags/functions not for dynamic
    ///linking.
    pub fn as_datafile<'a>( &'a mut self) -> &'a mut Self {
        self.x |=0x00000002u32;
        self
    }

    ///Library as Data File Exclusive
    ///
    ///Same as the above but this ensures you have WRITE permission to the
    ///virtual memory space the DLL/EXE is loaded into
    pub fn as_exclusive_datafile<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000040u32;
        self
    }

    ///Library as image resource
    ///
    ///Loads a DLL as a mapped file with read only access into memory. This
    ///will by-pass any and all startup routines the DLL may invoke.
    pub fn as_image_resource<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000020u32;
        self
    }

    ///Search Application Dir
    ///
    ///Allows the developer to not provide a path, but only a DLL name. The
    ///windows loader will search the local application directory for the 
    ///specified DLL name. 
    ///
    ///No other directories will be searched.
    pub fn search_application_dir<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000200u32;
        self
    }

    ///Search Default Dirs
    ///
    ///Search ANYWHERE the DLL could be. Application Dir, System32, User Dir,
    ///AND environment path descripted loations.
    pub fn search_default_dirs<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00001000u32;
        self
    }

    ///Search DLL Load Dir
    ///
    ///This modifies the state of future searches. If you find a DLL with this
    ///flag then only the directory specified is searched. No others.
    pub fn search_dll_load_dir<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000100u32;
        self
    }

    ///Search System32
    ///
    ///Search the %windows%\system32 directory for the DLL
    pub fn search_system32<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000800u32;
        self
    }

    ///Search user dirs
    ///
    ///Search directors that are added with `AddDllDirectory` or the
    ///`SetDllDirectory` functions. 
    pub fn search_user_dirs<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000400u32;
        self
    }

    ///Altered Search Path
    ///
    ///If you give me a full path I'm going to ignore it. You cannot combine
    ///this with any other search flag.
    pub fn altered_search_path<'a>(&'a mut self) -> &'a mut Self {
        self.x |= 0x00000008u32;
        self
    }

    ///Attempt the Load
    ///
    ///Give the system a path. This function will re-allocate the given
    ///string, to append a null terminator to the end. If you provide the
    ///full ensure you use the proper path seperator I.E.: '\' not the unix
    ///'/'. If you are not using rust's raw strings (str declartinon that
    ///starts with `r"`). You will need to use the `\\` so the backslash
    ///doesn't escape.
    ///
    ///#NOTE
    ///
    ///Only ASCII paths are supported. This is a limitation of the Rust
    ///StdLib `OsString` type. Windows has both UTF-16 and ASCII-8 string
    ///types.
    pub fn load(&self, path: &str)
    -> Result<DynLibWin,Error>
    {
        let mut os_str = OsString::from(path);
        os_str.push("\x00");
        unsafe{
            let (ptr,_,_): (*const i8,usize,usize) = mem::transmute(os_str);
            let handle = LoadLibraryExA(
                            ptr,
                            mem::transmute(0usize),
                            self.x);
            let val: usize = mem::transmute(handle);
            if val == 0usize {
                Err(Error::last_os_error())
            } else {
                Ok(DynLibWin{ handle: handle } )
            }
        }
    }
}
        

    

///DynLib
///
///The object that represents a dynamic library loaded into memory. This object
///is special to Microsoft Windows. You'll want to compile this program with
///the msvc tool chain. And when deploying ensure the Visual C++ runtime is
///deployed to the target machine.
///
///DynLib does not implement drop. So it will not be de-allocated. You must
///manually drop dynamic links.
pub struct DynLibWin {
    handle: HMODULE
}
impl DynLibWin {
    
    ///Load a function
    ///
    ///Ensure the names are mangled correctly. The null terminator will
    ///be appened in this function. The symbol you pass will be re-allocated
    ///within this function.
    ///
    ///One needs to cast the `VoidPtr` type to a a function. This is
    ///accomplished via the:
    ///
    ///`let func: extern "Rust" fn([YOUR ARGS] -> [YOUR RESULT] = unsafe{ mem::transmute([VOIDPTR]);`
    ///
    ///One should not the ABI is not burned in stone. You will have to
    ///change the value based on _what_ you are loading. The values
    ///supported by Rust are:
    ///
    ///`cdecl`, `stdcall`, `fastcall`, `vectorcall`, `aapcs`, ``win64`,
    ///`sysv64`, `Rust`, `C`, `system`, `rust-intrinsic`, `rust-all`, 
    ///and `platform-intrinsic`. Ensure your caps are correct.
    pub fn load_function(&self, symbol: &str)
    -> Result<VoidPtr,Error>
    {
        let mut os_str = OsString::from(symbol);
        os_str.push("\x00");
        unsafe {
            let (p,_,_): (*const i8, usize, usize) = mem::transmute(os_str);
            let ptr = GetProcAddress(
                            self.handle,
                            p);
            let val: usize = mem::transmute(ptr);
            if val == 0usize {
                Err(Error::last_os_error())
            } else {
                Ok(ptr)
            }
        }
    }

    ///Free the Library
    ///
    ///This will unload the DLL and invalidate all loaded functions. Use
    ///with care.
    pub fn free(self) -> Result<(),Error> {
        let handle = self.handle;
        let flag = unsafe{ FreeLibrary(handle) };
        if flag != 0 {
            Ok(())
        } else {
            Err(Error::last_os_error())
        }
    }
}



#[test]
fn test_it_all() {
    let p: &'static str = "C:\\users\\wlaeder\\documents\\rust_stuff\\dynlib\\test_dll.dll";
    let lib = match LoadWinDynLib::new()
        .load(p) {
        Ok(x) => x,
        Err(e) => {
            panic!("\n\n\nFailed to load library\n{:?}\n\n\n",e);
        }
    };
    println!("Loaded Library");
    let ptr = match lib.load_function("addition") {
        Ok(x) => x,
        Err(e) => {
            panic!("\n\n\nFailed to load symbol\n{:?}\n\n\n",e);
        }
    };
    println!("Found Symbol");
    let func: extern "Rust" fn(u64,u64)-> u64 = unsafe{ mem::transmute(ptr) };
    println!("Function Cast, calling...");
    assert_eq!(func(2,2), 4u64);
    println!("Success!");
}