Skip to content

Commit

Permalink
fix function call errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ReKreker authored and ReKreker committed Oct 15, 2022
1 parent 27bef19 commit bc71076
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 134 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ example/*.so
example/leet_add
example/*.o
internal/__pycache__
pypi/
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 1.1 (15 Oct 2022)
## Changes
- add changelog
- fix multiple call imported function error
- add ability to get return from import function
- add debug logs
- fix section create bug
- add common errors note

# 1.0
Public release
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
Tool for C-code injections in already compiled bins.

## Usage
Write hook config as described in the [instruction](docs/hooks%20xml.md).
Write hook config as described in the [instruction](https://github.com/ReKreker/shooker/blob/master/docs/hooks%20xml.md).

```shooker --xml config.xml target_dir/ output_dir/```

## Install
```pip install shooker```<br />
*Please read about [common errors](https://github.com/ReKreker/shooker/blob/master/docs/common%20errors.md)*

## Example
\> ```cd example/``` <br />
\> ```make compile``` <br />
Expand All @@ -18,20 +22,18 @@ LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:./ ./leet_add <br />
3713 <br />
\> ```make hook``` <br />
./../shooker ./ ./ <br />
Patching libtarget.so... <br />
Compiling hook for add_n_print <br />
Patching the hook(s)... <br />
Hooking add_n_print <br />
Lib(s) patched <br />
INFO: Patching libtarget.so... <br />
INFO: Compiling hook for add_n_print <br />
INFO: Patching the hook(s)... <br />
INFO: Hooking add_n_print <br />
INFO: Lib(s) patched <br />
\> ```make run``` <br />
LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:./ ./leet_add <br />
1337 <br />

## Install
```pip install shooker```
Leet is 1337 <br />

## To improve
- Add ability to inject to .exe/.dll
- Try to avoid sub-instruction patching mechanism in the hook(s)
- Add support of arm architecture
- Add support hooking raw binaries
- Add support hooking raw binaries
- Develop true hook(not replace)
8 changes: 8 additions & 0 deletions docs/common errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#### Multiple calls the same func at one line
```│translation.c:7:51: error: duplicate label ‘func_name_jmp_10’```

How to fix:
```printf(_s("Leet is %d\n"), arg1*100+arg2/100); printf(_s("Trigger error"));```
to
```printf(_s("Leet is %d\n"), arg1*100+arg2/100);
printf(_s("Test"));```
4 changes: 2 additions & 2 deletions example/hooks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<func proto="int FUNC(char *, ...)" kind="import">printf</func>
</include>
<hook proto="void FUNC(int arg1, int arg2)" name="add_n_print">
printf(_s("%d\n"), arg1*100+arg2/100);
printf(_s("Leet is %d\n"), arg1*100+arg2/100);
</hook>
</lib_hook>
</shook>
</shook>
38 changes: 23 additions & 15 deletions internal/asm.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import capstone

from internal.other import *

from lief.ELF import ARCH

from internal.other import *


class Assembler:
"""Arch-depended stuff"""
Expand All @@ -18,10 +18,15 @@ def jump(self, target_addr: int) -> bytes:
"""Create jmp-instruction to target_addr"""
if self.arch == ARCH.x86_64:
target_addr -= 5 # length of jmp instr
return b"\xE9" + target_addr.to_bytes(4, "little")
jump_inst = b"\xE9" + target_addr.to_bytes(4, "little")
else:
raise Unimplemented(f"Assemble jump for {arch}")

logging.debug(
f"Crafted jump-instruction is {jump_inst.hex()} for {hex(target_addr)}"
)
return jump_inst

def brute_ptl(self, section: LiefSect, target_addr: int) -> int:
"""Find plt-stub for specific function from .got.plt"""
cont = section.content.tobytes()
Expand All @@ -36,6 +41,7 @@ def brute_ptl(self, section: LiefSect, target_addr: int) -> int:

# offset from instruction + current entry plt position + len of jmp instruction
if instr_offs + instr.address + instr.size == target_addr:
logging.debug(f"Found plt for {hex(target_addr)}")
return instr.address
else:
raise Unimplemented(f"Enumerating plt for {self.arch}")
Expand All @@ -47,7 +53,7 @@ def patch_sub_values(self, func_cont: list, func_offs: int) -> list:

cont = bytearray(b"".join([i.to_bytes(1, "big") for i in func_cont]))
register = None
lea_ip = None
lea_ip = 0
sub_instr = None
call_ip = 0

Expand All @@ -57,34 +63,36 @@ def patch_sub_values(self, func_cont: list, func_offs: int) -> list:
if instr.mnemonic == "lea" and "[rip - 7]" in instr.op_str:
lea_ip = instr.address
register = instr.op_str.replace(", [rip - 7]", "")
logging.debug(
f"Register of lea instr found {register} at {hex(func_offs+lea_ip)}"
)

if (
instr.mnemonic == "sub"
and register is not None
and instr.op_str.startswith(register)
):
sub_instr = instr
logging.debug(f"Sub instr found at {hex(func_offs+instr.address)}")

if (
instr.mnemonic == "call"
and sub_instr is not None
and 2 <= instr.size <= 3
):
call_ip = instr.address
break
logging.debug(f"Call instr found at {hex(func_offs+call_ip)}")

if lea_ip == 0:
raise NotFound(f"Get IP instruction")
elif sub_instr is None:
raise NotFound(f"Sub {register}, #const")
elif call_ip == 0:
raise NotFound(f"Register-jump instruction")
if lea_ip != 0 and sub_instr is not None and call_ip != 0:
sub_value = int.from_bytes(sub_instr.bytes[2:], "little")
relative_offs = func_offs + lea_ip - sub_value
sub_bytes = sub_instr.bytes[:2] + relative_offs.to_bytes(4, "little")

sub_value = int.from_bytes(sub_instr.bytes[2:], "little")
relative_offs = func_offs + lea_ip - sub_value
sub_bytes = sub_instr.bytes[:2] + relative_offs.to_bytes(4, "little")
cont[sub_instr.address : sub_instr.address + sub_instr.size] = sub_bytes

cont[sub_instr.address : sub_instr.address + sub_instr.size] = sub_bytes
# zeroing to continue parse function
lea_ip, call_ip = 0, 0
register, sub_instr = None, None

else:
raise Unimplemented(f"Patch sub-instruction for {arch}")
Expand Down
26 changes: 22 additions & 4 deletions internal/cmpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(self, cc: str, path: Path) -> None:
self.whitelist_funcs = []

def include_lib(self, inc: str) -> None:
logging.debug(f"Include lib {inc}")
if not inc:
raise NotFound("Include lib")

Expand All @@ -25,17 +26,30 @@ def include_func(self, name: str, proto: str, addr: int) -> None:
if not name or not proto or not addr:
raise NotFound("Name/proto/addr for include func")

# use Labels as Values https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
decl = f"#define {name} {name}_jmp: (({proto.replace('FUNC', '(*)')})((long)&&{name}_jmp-{addr}))"
# use Labels as Values https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
# and Statements and Expressions https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
label = f"UNIQ_LINE({name}_jmp)"
decl = (
f"#define {name} (({proto.replace('FUNC', '(*)')})"
+ "({"
+ f"{label}:(long)&&{label}-{addr};"
+ "}))"
)
logging.debug(f"Include func {decl}")
self.inc_fncs.append(decl)

def assemble_transl(self) -> None:
"""Some asm-tricks"""
# stuff like #include
self.code += "\n".join(self.inc_libs) + "\n"
self.code += "\n".join(self.inc_libs)

# asm-trick to avoid broken xref for string from another segment
self.code += "#define _s(string) ((char *)(const char []){string})\n"
self.code += "\n#define _s(string) ((char *)(const char []){string})\n"

# asm-trick for unique jump label for relative jump funcs
self.code += "\n#define CONCAT_(prefix, suffix) prefix##suffix"
self.code += "\n#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)"
self.code += "\n#define UNIQ_LINE(prefix) CONCAT(prefix##_, __LINE__)\n"

# func declaration stuff
self.code += "\n".join(self.inc_fncs) + "\n"
Expand All @@ -58,6 +72,8 @@ def compile_transl(self, txt_addr: int) -> FuncsInfo:
# uncomment to look translation.c
# __import__("IPython").embed()

logging.debug("="*70 + "\n"+ self.code + "\n" + "="*70)

cmd = [
self.cc,
"-fPIC",
Expand Down Expand Up @@ -89,6 +105,7 @@ def compile_transl(self, txt_addr: int) -> FuncsInfo:
start = txt.virtual_address
end = start + txt.size
offset = 0
logging.debug(f"Translation offset {hex(start)}")

# extract bytes of each function
for sym in trs_bin.static_symbols:
Expand All @@ -97,6 +114,7 @@ def compile_transl(self, txt_addr: int) -> FuncsInfo:
and sym.value < end
and sym.name in self.whitelist_funcs
):
logging.debug(f"Created {sym.name} with size {sym.size}")
content = trs_bin.get_content_from_virtual_address(sym.value, sym.size)
funcs_info[sym.name] = {"content": content, "offset": offset}
offset += len(content)
Expand Down
2 changes: 2 additions & 0 deletions internal/ftb.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def load_symbol(self, fnc_name: str) -> int:

entry = FuncEntry(fnc.name, fnc.value)
self.fncs.append(entry)
logging.debug(f"Static {entry.name} loaded at {hex(entry.addr)}")
return fnc.value

def load_import(self, fnc_name: str) -> int:
Expand All @@ -32,6 +33,7 @@ def load_import(self, fnc_name: str) -> int:
jump_addr = self.asm.brute_ptl(plt, fnc.address)
entry = FuncEntry(fnc.symbol.name, jump_addr)
self.fncs.append(entry)
logging.debug(f"Import {entry.name} loaded at {hex(entry.addr)}")
return jump_addr


Expand Down
7 changes: 5 additions & 2 deletions internal/inj.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from internal.other import *

from lief.ELF import Section, SECTION_FLAGS, SECTION_TYPES

from internal.other import *


class Inject:
"""Create shook section and some hooking stuff"""
Expand All @@ -14,11 +14,14 @@ def __init__(self, target: LiefBin, asm) -> None:
def shook_sect_init(self) -> None:
"""Create section to find out base address for linker"""
if self.bin.get_section(".shook") is not None:
logging.debug("Section .shook exists")
return

logging.debug("Create .shook section")
section = Section(".shook", SECTION_TYPES.PROGBITS)
section += SECTION_FLAGS.EXECINSTR
section += SECTION_FLAGS.WRITE
section.content = [0]*0x500
self.bin.add(section, loaded=True)

def shook_sect_fill(self, content: list) -> None:
Expand Down
5 changes: 5 additions & 0 deletions internal/other.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import lief
import logging

from pathlib import Path
from typing import NewType, Dict, List
Expand All @@ -12,6 +13,7 @@
"NotFound",
"CompileFail",
"Wrong",
"logging",
]

LiefBin = NewType("liefBin", lief.ELF.Binary | lief.MachO.Binary | lief.PE.Binary)
Expand All @@ -37,3 +39,6 @@ def __init__(self, cmd):
class Wrong(Exception):
def __init__(self, msg):
pass


logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO)
Loading

0 comments on commit bc71076

Please sign in to comment.