Skip to content

Commit 7efb59b

Browse files
Adding documentation, fixing an issue with shellcode injection
1 parent a444f41 commit 7efb59b

17 files changed

+350
-101
lines changed

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ __pycache__/
263263
# Project & Solution Files
264264
*.sln
265265
*.vcxpro*
266+
*.cspro*
266267

267268
# Visual Studio Code configuration files
268269
*.vscode/
@@ -272,10 +273,9 @@ Makefile
272273
*.make
273274

274275
# Additional Files
275-
mediaLib/lib/*.lib
276-
htCodec/3rdParty/rans_static/lib/*
277276
enc_temp_folder/*
278277
builds/*
279278
* Advisor Result*/
280279
buildscripts/tmp/*
281280
buildscripts/csi/*
281+
*.bin.bak

README.md

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
2+
# llscript
3+
## What is it?
4+
- A basic custom low level scripting language, with a run-time-environment that can be injected as Shellcode.
5+
- The compiled bytecode can simply be appended to the run-time-environment shellcode to be executed when injected.
6+
- Includes a compiler (written in C#), a command line debugger (with custom debug information format) and a shellcode executer.
7+
- Can also be used as a very small, easily integratable, embedded-friendly run-time-environment for scripts that needs low level access.
8+
- Currently only works with Windows x64, but shouldn't be particularly hard to port.
9+
10+
## What can it do?
11+
- Load arbitrary DLLs and extract symbols.
12+
- Call C functions loaded from those DLLs
13+
- Basic programming stuff.
14+
15+
## What can't it do?
16+
- More advanced programming stuff that isn't absolutely essential.
17+
- It can't even do `for` loops, only `while`, no `structs` etc. but the compiler is very hackable.
18+
- Oh, and the shellcode isn't null free. One could hack the compiler to output null free byte code and spend the rest of eternity writing a null free interpreter, but that's not the point of this project at the moment.
19+
20+
## How to use it for shellcode?
21+
### Step 1: Write Your Script
22+
This is the example script. It only opens a `MessageBoxA`. But demonstrates how to load symbols from a DLL.
23+
```c++
24+
const text kernel32dll = "User32.dll"; // `text` maps to `ptr<i8>`.
25+
const text messageBoxA = "MessageBoxA";
26+
27+
// `load_library` is provided by the compiler.
28+
// other builtin functions include `alloc`, `free`, `realloc`, `get_proc_address`.
29+
voidptr kernel32dll_handle = load_library(kernel32dll); // `voidptr` maps to `ptr<void>`.
30+
voidptr messageBoxAAddr = get_proc_address(kernel32dll_handle, messageBoxA);
31+
32+
// this is how casts and pointers to external function work.
33+
extern_func<i32 (const voidptr, const text, const text, u32)> messageBoxAFunc =
34+
cast<extern_func<i32 (const voidptr, const text, const text, u32)>>(messageBoxAAddr);
35+
36+
messageBoxAFunc(null, "Hello from the other side!", "Very Important Message", 0);
37+
```
38+
39+
### Step 2: Compile the Script.
40+
```
41+
> llsc example.lls
42+
llsc - LLS Bytecode Compiler (Build Version: 1.0.8566.36389)
43+
44+
Parsing Succeeded. (88 Nodes parsed from 1 Files)
45+
46+
Warning (in 'example.lls', Line 13):
47+
lvalue call to 'extern_func<i32 (const ptr<void>, const ptr<const i8>, const ptr<const i8>, u32)> messageBoxAFunc' will discard the return value of type 'i32'.
48+
49+
Instruction Generation Succeeded. (69 Instructions & Pseudo-Instructions generated.)
50+
Code Generation Succeeded. (393 Bytes)
51+
Successfully wrote byte code to 'bytecode.lls'.
52+
53+
Compilation Succeeded.
54+
```
55+
56+
### Step 3: Append the Bytecode to the Run-Time-Environment Shellcode
57+
- Open the compiler output file `bytecode.lls` and the run-time-environment shellcode `script_host.bin` in a hex editor like [HxD](https://mh-nexus.de/en/hxd/).
58+
- Create a new hex-file and first paste in the contents of `script_host.bin`. This section should end with the magic constant `37 6F 63 03 12 9E 71 31`.
59+
- Now paste in the contents of `bytecode.lls`. These should usually begin with `0E` (which is the op code `LLS_OP_STACK_INC_IMM`).
60+
- Save the file.
61+
62+
### Step 4: Test the shellcode
63+
Use `runsc` to test your shellcode.
64+
```
65+
runsc <YourShellcodeFileName>
66+
```
67+
68+
## How to debug scripts?
69+
Debugging such a hacked runtime environment is obviously not as easy as your normal programming languages, but there's a command line (low level) debugger.
70+
71+
### Step 1: Compile with Debug-Info
72+
```
73+
> llsc example.lls -dbgdb
74+
llsc - LLS Bytecode Compiler (Build Version: 1.0.8566.36389)
75+
76+
Parsing Succeeded. (88 Nodes parsed from 1 Files)
77+
78+
Warning (in 'example.lls', Line 13):
79+
lvalue call to 'extern_func<i32 (const ptr<void>, const ptr<const i8>, const ptr<const i8>, u32)> messageBoxAFunc' will discard the return value of type 'i32'.
80+
81+
Instruction Generation Succeeded. (69 Instructions & Pseudo-Instructions generated.)
82+
Code Generation Succeeded. (393 Bytes)
83+
Successfully wrote byte code to 'bytecode.lls'.
84+
Successfully wrote debug database to 'bytecode.lls.dbg'.
85+
86+
Compilation Succeeded.
87+
```
88+
89+
### Step 2: Launch the Command Line Debugger
90+
```
91+
> llscript_dbg bytecode.lls bytecode.lls.dbg
92+
llshost byte code interpreter
93+
94+
'c' to run / continue execution
95+
'n' to step
96+
'l' to step a line (only available with debug info)
97+
'f' to step out
98+
'b' to set the breakpoint
99+
'r' for registers
100+
'p' for stack bytes
101+
'y' for advanced stack bytes
102+
'i' to inspect a value
103+
'm' to modify a value
104+
'v' show recent values (only available with debug info)
105+
'o' clear recent values (only available with debug info)
106+
'w' set value filter (only available with debug info)
107+
'W' break on a value filter match (only available with debug info)
108+
'F' continue to next function call/return
109+
's' toggle silent
110+
'S' toggle silent comments
111+
'q' to restart
112+
'x' to quit
113+
'z' to debug break
114+
115+
116+
File: example.lls
117+
1: const text kernel32dll = "User32.dll"; // `text` maps to `ptr<i8>`.
118+
>>
119+
```
120+
121+
Now press <kbd>l</kbd> to step line by line, <kbd>c</kbd> to run.
122+
Recently modified values and associated lines in the script will be displayed above the input line if debug information is available.
123+
124+
```
125+
kernel32dll @ code base offset 320 (array<i8>) : 0x7FF50BC00140
126+
--> 85, 115, 101, 114, 51, 50, 46, 100, 108, 108, 0, 77, 101, 115, 115, 97, 103, 101, 66, 111, 120, 65, 0, 72, ...
127+
--> 0x55, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x0, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x0, 0x48, ...
128+
--> "User32.dll"
129+
messageBoxA @ code base offset 331 (array<i8>) : 0x7FF50BC0014B
130+
--> 77, 101, 115, 115, 97, 103, 101, 66, 111, 120, 65, 0, 72, 101, 108, 108, 111, 32, 102, 114, 111, 109, 32, 116, ...
131+
--> 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x0, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x74, ...
132+
--> "MessageBoxA"
133+
6: voidptr kernel32dll_handle = load_library(kernel32dll); // `voidptr` maps to `ptr<void>`.
134+
```
135+
136+
## How to integrate it with another application?
137+
Simply include the corresponding header, link to `script_host.lib` and start the run-time-environment with a pointer to the bytecode.
138+
139+
```c++
140+
llshost_state_t state = {};
141+
state.pCode = your_compiled_byte_code;
142+
143+
// If you need to pass in additional values, simply set register values:
144+
state.registerValues[0] = (uint64_t)value0;
145+
state.registerValues[1] = (uint64_t)value1;
146+
147+
// Now, start the runtime-environment.
148+
llshost_from_state(&state);
149+
```
150+
151+
## How to build it?
152+
- Clone the repo `git clone https://github.com/rainerzufalldererste/llscript.git`
153+
- Run `create_project.bat`, select `Visual Studio 2015` if you're trying to build the script host for shellcode (evenything newer will produce calls to `memcpy` even without the `crt`. If you're just planning to embed the script host in another application, any new Visual Studio version is fine.
154+
155+
If you just want to play with the debugger or compiler, you can simply use `MSBuild` or `Visual Studio` to build the project solution.
156+
157+
If you want to create a shellcode version of a modified run-time-environment:
158+
159+
- Build `llscript_asm` then `llscript_host` then `llscript_host_bin`. (Visual Studio sometimes gets confused about `llscript_asm`, so not relying on dependencies is probably a good idea)
160+
- Extract the `.code` section via some dumping tool or simply open the binary in IDA, go to the last statement of the last symbol, and select everything upwards in the hex view, then paste that into a hex editor.
161+
- Now we need to patch the assembly, because getting the current `rip` isn't something that can be expressed in a position independent or overly complicated way in msvc afaik, so we'll need to replace the assembly generated for `uint8_t *pCode = __readgsqword(0);` with `lea <whatever register the compiler chose>, [rip]`.
162+
-- if the register was `rax`, replace `65 48 8B 04 25 00 00 00 00` (`mov rax,qword ptr gs:[0]`) with `48 8D 05 00 00 00 00 90 90`.
163+
-- if the register was `rax`, replace `65 48 8B 0C 25 00 00 00 00` (`mov rcx,qword ptr gs:[0]`) with `48 8D 0D 00 00 00 00 90 90`.
164+
-- if the register was anything else, use the [defuse.ca online x64 assembler](https://defuse.ca/online-x86-assembler.htm) and assemble `lea <whatever register the compiler chose>, [rip]` and replace the corresponding code with whatever that says. Pad with `0x90` (`nop`).
165+
- Lastly, append the magic constant (`37 6F 63 03 12 9E 71 31`) to the shellcode. The runtime-environment will search for this pattern when launched without code to find it's input.
166+
167+
## License
168+
- MIT

create_project.bat

+31-8
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,51 @@ IF [%1]==[] GOTO MANUAL_CONFIG
44

55
IF "%1"=="1" GOTO ONE;
66
IF "%1"=="2" GOTO TWO;
7+
IF "%1"=="3" GOTO THREE;
8+
IF "%1"=="4" GOTO FOUR;
9+
IF "%1"=="5" GOTO FIVE;
710

811
ECHO INVALID PARAMETER (%1)
912

1013
:MANUAL_CONFIG
11-
ECHO 1. Visual Studio 2019 Solution
12-
ECHO 2. Visual Studio 2015 Solution
14+
ECHO 1. Visual Studio 2022 Solution
15+
ECHO 2. Visual Studio 2019 Solution
16+
ECHO 3. Visual Studio 2017 Solution
17+
ECHO 4. Visual Studio 2015 Solution
18+
ECHO 5. Visual Studio 2013 Solution
1319

14-
CHOICE /N /C:12 /M "[1-2]:"
20+
CHOICE /N /C:12345 /M "[1-5]:"
1521

22+
IF ERRORLEVEL ==5 GOTO FIVE
23+
IF ERRORLEVEL ==4 GOTO FOUR
24+
IF ERRORLEVEL ==3 GOTO THREE
1625
IF ERRORLEVEL ==2 GOTO TWO
1726
IF ERRORLEVEL ==1 GOTO ONE
1827
GOTO END
1928

20-
:TWO
29+
:FIVE
30+
ECHO Creating VS2013 Project...
31+
premake\premake5.exe vs2013
32+
GOTO END
33+
34+
:FOUR
2135
ECHO Creating VS2015 Project...
22-
premake\premake5.exe vs2015 %2 %3 %4 %5 %6 %7
36+
premake\premake5.exe vs2015
2337
GOTO END
2438

25-
:ONE
39+
:THREE
40+
ECHO Creating VS2017 Project...
41+
premake\premake5.exe vs2017
42+
GOTO END
43+
44+
:TWO
2645
ECHO Creating VS2019 Project...
27-
premake\premake5.exe vs2019 %2 %3 %4 %5 %6 %7
46+
premake\premake5.exe vs2019
2847
GOTO END
2948

30-
:END
49+
:ONE
50+
ECHO Creating VS2022 Project...
51+
premake\premake5.exe vs2022
52+
GOTO END
3153

54+
:END

example.lls

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const text kernel32dll = "User32.dll"; // `text` maps to `ptr<i8>`.
2+
const text messageBoxA = "MessageBoxA";
3+
4+
// `load_library` is provided by the compiler.
5+
// other builtin functions include `alloc`, `free`, `realloc`, `get_proc_address`.
6+
voidptr kernel32dll_handle = load_library(kernel32dll); // `voidptr` maps to `ptr<void>`.
7+
voidptr messageBoxAAddr = get_proc_address(kernel32dll_handle, messageBoxA);
8+
9+
// this is how casts and pointers to external function work.
10+
extern_func<i32 (const voidptr, const text, const text, u32)> messageBoxAFunc =
11+
cast<extern_func<i32 (const voidptr, const text, const text, u32)>>(messageBoxAAddr);
12+
13+
messageBoxAFunc(null, "Hello from the other side!", "Very Important Message", 0);

example_shellcode.bin

4.57 KB
Binary file not shown.

llsc/app.config

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

llsc/llsc.csproj

-71
This file was deleted.

0 commit comments

Comments
 (0)