OffensiveZig
OffensiveZig copied to clipboard
Some attempts at using Zig(https://ziglang.org/) in penetration testing.
OffensiveZig
The purpose of this project is to do some experiments with Zig, and to explore the possibility of using it for implant development and general offensive operations. it is inspired by @byt3bl33d3r's project "OffensiveNim".
Table of Contents
- OffensiveZig
- Table of Contents
- Why Zig?
- Try to Learn Zig in Y minutes
- How to play
- Cross Compiling
- Interfacing with C/C++
- Creating Windows DLLs with an exported
DllMain - Optimizing executables for size
- Executable size difference when using the 3rd-party windows library vs without
- Opsec Considerations
- Converting C code to Zig
- Language Bridges
- Debugging
- Setting up a dev environment
- Interesting Zig libraries
- Zig for implant dev links
- Comparison of Zig and Nim
- Summary
Why Zig?
- Zig's backend using LLVM and also can be as a C compiler, Nim can use it to compile. [ wikipedia ]
- The feature about generation of human-readable C code may be supported in the future. [ issue #3772 ]
- Zig doesn't rely on a VM/runtime, the static executable is super small.
- Rust inspired syntax, allows rapid native payload creation & prototyping.
- Integration with C libraries without FFI/bindings.
- Manual memory management, no hidden control flow.
- Its very easy to do cross-compile things and can build for any of the targets from here, no "cross toolchain" needs to be installed or anything like that.
- To Learn
zig zen: together we serve the users.
Try to Learn Zig in Y minutes
Nim tutorials are available at Learn X in Y Minutes and it also has a Book named Nim in Action, but Zig doesn't. You can find lots of content referenced from https://ziglearn.org & Zig's main developer youtube channel https://www.youtube.com/channel/UCUICU6mgcyGy61pojwuWyHA*
How to play
Examples in this project
| File | Description |
|---|---|
pop_bin.zig |
Call MessageBox WinApi without using the 3rd-party library |
pop_zigwin32_bin.zig |
Call MessageBox with the Zig-win32 libary |
pop_zigwin32_lib.zig |
Example of creating a Windows DLL with an exported DllMain |
wmiquery_bin.zig |
Queries running processes and installed AVs using using WMI |
shellcode_bin.zig |
Creates a suspended process and injects shellcode with VirtualAllocEx/CreateRemoteThread. Also demonstrates the usage of compile time definitions to detect arch, os etc.. |
http_request_bin.zig |
Demonstrates a couple of ways of making HTTP requests |
I recommand install zig from a Package Manager. Some of the examples in this project use third-party libraries, unfortunately Zig does not provide official package management at the moment, You need to follow the README.md for using of the third library, or use a package management tool that is in the prototype stage called zkg.
Cross Compiling
See the cross-compilation section in the Zig compiler usage guide, for a lot more details.
Cross compiling to Windows from MacOs/Nix: zig build-exe -target x86_64-windows src.zig
Interfacing with C/C++
See the insane Integration with C section in the Zig document.
Here's MessageBox example
const std = @import("std");
usingnamespace std.os.windows;
extern "user32" fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) callconv(.Stdcall) c_int;
pub fn main() void {
_ = MessageBoxA(null, "hello,world!", "title", 0);
}
Creating Windows DLLs with an exported DllMain
See the insane Building a library section.
As you can see, the code in the example is already very close to what C code looks like, just use export keyword.
Example:
const std = @import("std");
const builtin = @import("builtin");
usingnamespace std.os.windows;
extern "user32" fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
const DLL_PROCESS_ATTACH = 1;
const DLL_THREAD_ATTACH = 2;
const DLL_THREAD_DETACH = 3;
const DLL_PROCESS_DETACH = 0;
export fn hello(data: *c_void, size: i32) i32 {
_ = MessageBoxA(null, "hello, Im in Exported Function", "title", 0);
return 0;
}
pub export fn DllMain(hInstance: HINSTANCE, ul_reason_for_call: DWORD, lpReserved: LPVOID) BOOL {
switch(ul_reason_for_call) {
DLL_PROCESS_ATTACH => {
_ = MessageBoxA(null, "hello, Im in DllMain", "title", 0);
},
DLL_THREAD_ATTACH => {},
DLL_THREAD_DETACH => {},
DLL_PROCESS_DETACH =>{},
else => {},
}
return 1;
}
To compile:
//To make a static library
zig build-lib test.zig -target x86_64-windows
//To make a shared library
zig build-lib test.zig -dynamic -target x86_64-windows
To Check:
dumpbin.exe /exports test.dll
Microsoft (R) COFF/PE Dumper Version 14.28.29334.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file test.dll
File Type: DLL
Section contains the following exports for test.dll
00000000 characteristics
0 time date stamp
0.00 version
0 ordinal base
10 number of functions
9 number of names
ordinal hint RVA name
1 0 00001AA0 DllMain
2 1 00001B20 _DllMainCRTStartup
3 2 00041000 __xl_a
4 3 00041008 __xl_z
5 4 00042010 _tls_end
6 5 0003D018 _tls_index
7 6 00042000 _tls_start
8 7 0003A270 _tls_used
9 8 00001A50 hello
......
Optimizing executables for size
Taken from the Build Mode
For the biggest size decrease use the following flags --release-small --strip --single-threaded
a full example for compile windows executable on Macos: zig build-exe src.zig -O ReleaseSmall --strip --single-threaded -target x86_64-windows
Executable size difference when using the 3rd-party windows library vs without
Incredibly enough the size difference is pretty negligible. Especially when you apply the size optimizations outlined above.
Zig has own build system, it provides a cross-platform, dependency-free way to declare the logic required to build a project. use zig init-exe command will generate build.zig file is automatically. for using 3rd-party lib "Zig-win32", edit your build.zig,or copy the zig-win32 folder to the root folder your zig source code, Zig cannot import module from the parent folder of src.
The two examples pop_bin.zig and pop_zigwin32_bin.zig were created for this purpose. lets have a look:
% ll
- rwx r-x r-x darkray staff 253.50K 25.Nov'20 15:42 >_ pop_bin.exe
- rwx r-x r-x darkray staff 253.50K 25.Nov'20 16:18 >_ pop_zigwin32_bin.exe
There seems to be no difference in size from the above results.
Opsec Considerations
All samples are compiled in this mode zig build-exe src.zig -O ReleaseSmall --strip --single-threaded -target x86_64-windows
Aside from a few specific NT functions found in the import table, I have not been able to find any other significant features that would indicate that they were coded in Zig.

Converting C code to Zig
Zig already provides the ability to translate C to Zig code, just try zig translate-c
Used it to translate a bunch of small C snippets, I haven't tried using this feature yet.
Language Bridges
About python module or Java JNI, I have not tried it yet.
Ref:
- https://github.com/kristoff-it/zig-cuckoofilter/
- https://github.com/ziglang/zig/issues/5795
- https://lists.sr.ht/~andrewrk/ziglang/%20%3CCACZYt3T8jACL+3Z_NMW8yYvcJ+5oyP%3Dh1s2HHdDL_VxYQH5rzQ%40mail.gmail.com%3E
Debugging
Use the function of std.debug namespace to show the call stack, and there is no IDE has good support for Zig's Debugging yet. If you use VSCode, try this extension ( webfreak.debug ), plz check this link[ 1 ][ 2 ]for more details.
Setting up a dev environment
VSCode also provides two extensions ( tiehuis.zig & lorenzopirro.zig-snippets ) to support Zig, although the functionality is very limited at this time.
Interesting Zig libraries
- https://github.com/GoNZooo/zig-win32
- https://github.com/Vexu/routez
- https://github.com/ducdetronquito/requestz
- https://github.com/ducdetronquito/h11
- https://github.com/ducdetronquito/http
- https://github.com/MasterQ32/zig-network
- https://github.com/lithdew/pike
- https://github.com/Hejsil/zig-clap
- https://github.com/Vexu/bog
- https://github.com/tiehuis/zig-regex
- https://github.com/alexnask/interface.zig
- https://github.com/marler8997/zig-os-windows
- https://github.com/nrdmn/awesome-zig
Zig for implant dev links
- https://github.com/Sobeston/injector
Comparison of Zig and Nim
| Zig | Nim | |
|---|---|---|
| Syntax Styles | like Rust | like Python |
| Backend | LLVM or Self-hosted | Others C Compiler or Self-Hosted |
| Code Generate | Support in future | Supported |
| Standard Library | General | Numerous |
| Memory Management | Manual | Multi-paradigm GC |
| FFI | Directly | Support |
| Translate C to ThisLang | Official | Third-Party |
| Package Manager | N/A | Nimble |
| Cross Compile | Convenient | Convenient |
| Executable Size (windows x86 debug mode) | ~200K | ~300K |
| Learning Curve | Not so easy | Easy |
| Community Resources | Poor | Rich |
Summary
In summary, I wouldn't be willing to use Zig as my primary Offensive language at this time,I've also looked at similar languages, such as Vlang, but still haven't started trying them out. Nim has better community resources and clearer documentation than Zig, reading the documentation became a pain when trying to get Zig to do the same job. Manual memory management is not very friendly for non-professional developers, so maybe when Zig stabilizes in the future, I'll be more willing to use it for some development work in penetration testing.
ps: I am not a pro developer, this project is only from the perspective of a penetration testing engineer. The above opinions are my own, plz correct me if theres any errors :)