Skip to content

Instantly share code, notes, and snippets.

@codehz

codehz/LICENSE Secret

Last active November 10, 2020 03:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codehz/38972dec42cb8ce9e400fbbc2967183c to your computer and use it in GitHub Desktop.
Save codehz/38972dec42cb8ce9e400fbbc2967183c to your computer and use it in GitHub Desktop.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
const std = @import("std");
extern "kernel32" fn GetCommandLineW() callconv(.Stdcall) [*:0]const u16;
extern "kernel32" fn Wow64DisableWow64FsRedirection(key: **c_void) callconv(.Stdcall) bool;
extern "shell32" fn ShellExecuteExW(info: *ShellExecuteInfo) callconv(.Stdcall) bool;
extern "ole32" fn CoInitializeEx(reserved: ?*c_void, flags: u32) callconv(.Stdcall) u32;
extern "kernel32" fn ExitProcess(code: c_uint) callconv(.Stdcall) noreturn;
extern "user32" fn MessageBoxA(
hwnd: usize,
text: ?[*:0]const u8,
caption: ?[*:0]const u8,
typ: c_uint,
) callconv(.Stdcall) c_int;
const ShellExecuteInfo = extern struct {
cbSize: u32 = @sizeOf(@This()),
fmask: u32 = 0,
hwnd: usize = 0,
verb: ?[*:0]const u16 = null,
file: ?[*:0]const u16 = null,
parameters: ?[*:0]const u16 = null,
directory: ?[*:0]const u16 = null,
show: c_int = 0,
app: usize = 0,
idlist: ?*c_void = null,
class: ?[*:0]const u16 = null,
hkey: usize = 0,
hotkey: u32 = 0,
moniter: usize = 0,
process: usize = 0,
};
const L = std.unicode.utf8ToUtf16LeStringLiteral;
fn l(ch: u8) u16 {
return @intCast(u16, ch);
}
// Assume only ascii
pub fn panic(name: []const u8, bt: ?*std.builtin.StackTrace) noreturn {
const G = struct {
var buffer = [1]u8{0} ** 1024;
};
for (name) |ch, i| G.buffer[i] = ch;
_ = MessageBoxA(0, G.buffer[0..:0], "ShellExecute panicked", 0x10);
ExitProcess(2);
}
fn help() noreturn {
const message =
\\Usage: shellexecute [/class <class>] [/verb <operation>] [/dir workdir] [/show <showflag>] [/silent] [/wow64] file [arguments...]
\\
\\ - class: ProgId or URI protocol or file extension or registry path under HKEY_CLASSES_ROOT
\\ - operation: edit|explore|find|open|properties|print|runas or any custom operations
\\ - showflag:
\\ - 0: HIDE (default)
\\ - 1: SHOWNORMAL
\\ - 2: SHOWMINIMIZED
\\ - 3: MAXIMIZE | SHOWMAXIMIZED
\\ - 4: SHOWNOACTIVATE
\\ - 5: SHOW
\\ - 6: MINIMIZE
\\ - 7: SHOWMINNOACTIVE
\\ - 8: SHOWNA
\\ - 9: RESTORE
\\ - 10: SHOWDEFAULT
;
_ = MessageBoxA(0, message, "ShellExecute", 0x241020);
ExitProcess(1);
}
// UNSAFE
const Buffer16 = struct {
const BUFFER_SIZE = 16384;
const BufferRaw = [BUFFER_SIZE]u16;
var buffer: BufferRaw = undefined;
idx: usize = 0,
fn init() @This() {
return .{};
}
fn push(self: *@This(), ch: u16) void {
buffer[self.idx] = ch;
self.idx += 1;
}
fn dump(self: *@This()) [:0]const u16 {
buffer[self.idx] = 0;
return buffer[0..self.idx :0];
}
};
const ArgIteratorW = struct {
index: usize,
cmd_line: [*:0]const u16,
fn init() @This() {
return @This(){
.index = 0,
.cmd_line = GetCommandLineW(),
};
}
const DataWithIndex = struct {
index: usize,
data: [:0]const u16,
cvt: bool,
};
fn lower16(ch: u16, cvt: bool) u16 {
return if (cvt and ch > l('A') and ch < l('Z')) ch ^ 0x20 else ch;
}
fn next(self: *@This()) ?DataWithIndex {
var cvt = false;
while (true) : (self.index += 1) {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return null,
l(' '), l('\t') => continue,
l('/') => {
cvt = true;
continue;
},
else => break,
}
}
const cache = self.index;
return DataWithIndex{
.index = cache,
.data = self.internalNext(cvt),
.cvt = cvt,
};
}
fn internalNext(self: *@This(), cvt: bool) [:0]const u16 {
var buf = Buffer16.init();
var backslash_count: usize = 0;
var in_quote = false;
while (true) : (self.index += 1) {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return buf.dump(),
l('"') => {
const quote_is_real = backslash_count % 2 == 0;
self.emitBackslashes(&buf, backslash_count / 2);
backslash_count = 0;
if (quote_is_real) {
in_quote = !in_quote;
} else {
buf.push(l('"'));
}
},
l('\\') => {
backslash_count += 1;
},
l(' '), l('\t') => {
self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
if (in_quote) {
buf.push(byte);
} else {
return buf.dump();
}
},
else => {
self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
buf.push(lower16(byte, cvt));
},
}
}
}
fn emitBackslashes(self: *@This(), buf: *Buffer16, emit_count: usize) void {
var i: usize = 0;
while (i < emit_count) : (i += 1) {
buf.push(l('\\'));
}
}
};
fn parseIntU16(input: []const u16) c_int {
var ret: c_int = 0;
for (input) |ch| {
if (ch >= l('0') and ch <= l('9')) {
var v = @intCast(c_int, ch - l('0'));
ret = ret * 10 + v;
} else @panic("Invalid number");
}
return ret;
}
const FixedAllocator = struct {
memory: [1048576]u16 = undefined,
used: usize = 0,
fn dupe(self: *@This(), input: []const u16) [:0]const u16 {
defer self.used += input.len + 1;
std.mem.copy(u16, self.memory[self.used .. self.used + input.len], input);
self.memory[self.used + input.len] = 0;
return self.memory[self.used .. self.used + input.len :0];
}
};
var fixed = FixedAllocator{};
const ExecInfo = struct {
options: Options,
file: ?[:0]const u16,
parameters: ?[*:0]const u16,
const Options = struct {
class: ?[:0]const u16 = null,
verb: ?[:0]const u16 = null,
dir: ?[:0]const u16 = null,
show: c_int = 0,
silent: bool = false,
wow64: bool = false,
};
fn init() ExecInfo {
var iter = ArgIteratorW.init();
_ = iter.next();
var options: Options = .{};
const fields: []const std.builtin.TypeInfo.StructField = std.meta.fields(Options);
var has_argument = false;
while (iter.next()) |arg| {
if (arg.cvt) {
has_argument = true;
var ok = false;
inline for (fields) |field| {
if (eq(field.name, arg.data)) {
switch (field.field_type) {
?[:0]const u16 => {
const val = iter.next() orelse @panic(field.name ++ " argument required");
@field(options, field.name) = fixed.dupe(val.data);
},
c_int => {
const val = iter.next() orelse @panic(field.name ++ " argument required");
if (val.data.len > 8) @panic("invalid number for " ++ field.name);
@field(options, field.name) = parseIntU16(val.data);
},
bool => @field(options, field.name) = true,
else => unreachable,
}
ok = true;
break;
}
}
if (!ok) help();
} else {
const file = fixed.dupe(arg.data);
return ExecInfo{
.options = options,
.file = file,
.parameters = if (iter.next()) |obj| iter.cmd_line + obj.index else null,
};
}
}
if (has_argument)
return ExecInfo{
.options = options,
.file = null,
.parameters = null,
};
help();
}
fn buildShellExecuteInfo(self: *const @This()) ShellExecuteInfo {
if (self.options.wow64) {
var key: *c_void = undefined;
_ = Wow64DisableWow64FsRedirection(&key);
}
var ret: ShellExecuteInfo = .{
.show = self.options.show,
.file = if (self.file) |file| file.ptr else null,
.parameters = self.parameters,
};
if (self.options.class) |class| {
ret.fmask |= 0x00000001; // SEE_MASK_CLASSNAME
ret.class = class.ptr;
}
if (self.options.silent) {
ret.fmask |= 0x00000400; // SEE_MASK_FLAG_NO_UI
}
if (self.options.verb) |verb| {
ret.verb = verb.ptr;
}
if (self.options.dir) |dir| {
ret.directory = dir.ptr;
}
return ret;
}
};
fn eq(comptime precompiled: []const u8, text: []const u16) bool {
return std.mem.eql(u16, text, L(precompiled));
}
pub fn main() anyerror!void {
_ = CoInitializeEx(null, 0x6);
const einfo = ExecInfo.init();
var info = einfo.buildShellExecuteInfo();
_ = ShellExecuteExW(&info);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment