Skip to content

Commit 3f63ffb

Browse files
committed
[Draft] initial scaffolding for the AVR target
Current State ------------- 1) Works with only the latest versions of LLVM (11 and above) 2) the instructions so far implemented: ADC, ADD, LDI Design Issues ------------- A unique feature of AVR is that it maps registers directly to RAM, thus we theoretically can't represent registers as variables, but instead shall model them as memory addresses[^1]. Treating this AVR feature fairly, will render quite hard to read and to analyze code. After digging through the forums, datasheets, compilers source code, and grepping avr-objdump outputs, we can presume that C compilers are not leveraging this feature and use normal reads and writes to access registers, instead of loads and stores. And the memory-mapping of registers is mostly reserved RAM-less AVR boards, where 32 register plays the role of minimal RAM. Of course, it doesn't mean that the malicious code can't exploit this. With this in mind, we still decided to model AVR as normal register-based machine but add a command-line option later, that will enable a conservative model, that lacks registers. Formally, our current lifter implementation (the one with registers) just assumes that all addresses in load and stores operation are greater than 32. [^1]: And yes, `*0 = 'B'` is a perfectly valid code on AVR that writes `0x42` into the `R0` register.
1 parent d23861c commit 3f63ffb

File tree

7 files changed

+299
-0
lines changed

7 files changed

+299
-0
lines changed

lib/bap_avr/bap_avr_target.ml

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
open Core_kernel
2+
open Bap_core_theory
3+
4+
let package = "bap"
5+
6+
type r16 and r8
7+
8+
type 'a bitv = 'a Theory.Bitv.t Theory.Value.sort
9+
10+
let r16 : r16 bitv = Theory.Bitv.define 16
11+
let r8 : r8 bitv = Theory.Bitv.define 8
12+
let bool = Theory.Bool.t
13+
14+
let reg t n = Theory.Var.define t n
15+
16+
let array ?(index=string_of_int) t pref size =
17+
List.init size ~f:(fun i -> reg t (pref ^ index i))
18+
19+
let untyped = List.map ~f:Theory.Var.forget
20+
let (@<) xs ys = untyped xs @ untyped ys
21+
22+
let gpr = array r8 "R" 32
23+
let sp = reg r16 "SP"
24+
let flags = List.map ~f:(reg bool) [
25+
"CF"; "ZF"; "NF"; "VF"; "SF"; "HF"; "TF"; "IF"
26+
]
27+
28+
let datas = Theory.Mem.define r16 r8
29+
let codes = Theory.Mem.define r16 r16
30+
31+
let data = reg datas "data"
32+
let code = reg codes "code"
33+
34+
let parent = Theory.Target.declare ~package "avr"
35+
~bits:8
36+
~byte:8
37+
~endianness:Theory.Endianness.le
38+
39+
40+
let atmega328 = Theory.Target.declare ~package "ATmega328"
41+
~parent
42+
~data
43+
~code
44+
~vars:(gpr @< [sp] @< flags @< [data] @< [code])
45+
46+
47+
let llvm_avr16 = Theory.Language.declare ~package "llvm-avr16"

lib/bap_avr/bap_avr_target.mli

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
open Bap_core_theory
2+
3+
val parent : Theory.target
4+
val atmega328 : Theory.target
5+
val llvm_avr16 : Theory.language
6+
7+
type r16 and r8
8+
9+
type 'a bitv = 'a Theory.Bitv.t Theory.Value.sort
10+
11+
val r16 : r16 bitv
12+
val r8 : r8 bitv
13+
14+
val code : (r16, r16) Theory.Mem.t Theory.var
15+
val data : (r16, r8) Theory.Mem.t Theory.var
16+
val gpr : r8 Theory.Bitv.t Theory.var list
17+
val sp : r16 Theory.Bitv.t Theory.var
18+
val flags : Theory.Bool.t Theory.var list

oasis/avr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Flag avr
2+
Description: Build Avr lifter
3+
Default: false
4+
5+
Library "bap-avr"
6+
Build$: flag(everything) || flag(avr)
7+
XMETADescription: common definitions for Avr targets
8+
Path: lib/bap_avr
9+
BuildDepends: core_kernel, bap-knowledge, bap-core-theory
10+
FindlibName: bap-avr
11+
Modules: Bap_avr_target
12+
13+
Library avr_plugin
14+
XMETADescription: provide Avr lifter
15+
Path: plugins/avr
16+
Build$: flag(everything) || flag(avr)
17+
BuildDepends: core_kernel, ppx_jane, ogre,
18+
bap-core-theory, bap-knowledge, bap-main,
19+
bap, bap-avr, bitvec
20+
FindlibName: bap-plugin-avr
21+
InternalModules: Avr_main, Avr_lifter
22+
XMETAExtraLines: tags="avr, lifter, atmega"

plugins/avr/.merlin

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
B ../../lib/bap_avr
2+
REC

plugins/avr/avr_lifter.ml

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
open Core_kernel
2+
open Bap_core_theory
3+
open Bap.Std
4+
5+
open KB.Syntax
6+
include Bap_main.Loggers()
7+
8+
module Target = Bap_avr_target
9+
module MC = Disasm_expert.Basic
10+
11+
type r1
12+
type 'a bitv = 'a Theory.Bitv.t Theory.Value.sort
13+
14+
let r1 : r1 bitv = Theory.Bitv.define 1
15+
16+
let make_regs regs =
17+
let regs =
18+
List.mapi regs ~f:(fun i r -> (i,r)) |>
19+
Map.of_alist_exn (module Int) in
20+
Map.find_exn regs
21+
22+
let gpr = make_regs Target.gpr
23+
let regnum s = Scanf.sscanf s "R%d" ident
24+
25+
let require_gpr insn pos f =
26+
match (MC.Insn.ops insn).(pos) with
27+
| Op.Reg r -> f (gpr (regnum (Reg.name r)))
28+
| _ -> KB.return Insn.empty
29+
30+
let require_imm insn pos f =
31+
match (MC.Insn.ops insn).(pos) with
32+
| Op.Imm x -> f (Option.value_exn (Imm.to_int x))
33+
| _ -> KB.return Insn.empty
34+
35+
let hf = Theory.Var.define r1 "HF"
36+
let cf = Theory.Var.define r1 "CF"
37+
let nf = Theory.Var.define r1 "NF"
38+
let vf = Theory.Var.define r1 "VF"
39+
let sf = Theory.Var.define r1 "SF"
40+
let zf = Theory.Var.define r1 "ZF"
41+
42+
module M8 = Bitvec.M8
43+
44+
module Avr(CT : Theory.Core) = struct
45+
open Target
46+
let rec seq = function
47+
| [] -> CT.perform Theory.Effect.Sort.bot
48+
| [x] -> x
49+
| x :: xs -> CT.seq x @@ seq xs
50+
51+
let const x = CT.int r8 (M8.int x)
52+
53+
let bit0 = CT.int r1 (Bitvec.M1.bool false)
54+
let bit1 = CT.int r1 (Bitvec.M1.bool true)
55+
let flag x = CT.ite x bit1 bit0
56+
57+
let nth x n =
58+
CT.(extract r1 (const n) (const n) x)
59+
60+
let data xs =
61+
KB.Object.create Theory.Program.cls >>= fun lbl ->
62+
CT.blk lbl (seq xs) (seq [])
63+
64+
let (&&) = CT.logand
65+
let (||) = CT.logor
66+
let not = CT.not
67+
68+
let bit reg n body =
69+
nth CT.(var reg) n >>= fun expr ->
70+
Theory.Var.scoped r1 @@ fun v ->
71+
CT.let_ v !!expr (body (CT.var v))
72+
73+
let halfcarry ~r ~rd ~rr =
74+
bit r 3 @@ fun r3 ->
75+
bit rd 3 @@ fun rd3 ->
76+
bit rr 3 @@ fun rr3 ->
77+
rd3 && rr3 || rr3 && not r3 || not r3 && rd3
78+
79+
let fullcarry ~r ~rd ~rr =
80+
bit r 7 @@ fun r7 ->
81+
bit rd 7 @@ fun rd7 ->
82+
bit rr 7 @@ fun rr7 ->
83+
rd7 && rr7 || rr7 && not r7 || r7 && not rd7
84+
85+
let overflow ~r ~rd ~rr =
86+
bit r 7 @@ fun r7 ->
87+
bit rd 7 @@ fun rd7 ->
88+
bit rr 7 @@ fun rr7 ->
89+
rd7 && rr7 && not r7 || not rd7 && not rr7 && r7
90+
91+
92+
let with_result rd f =
93+
Theory.Var.fresh (Theory.Var.sort rd) >>= fun v ->
94+
f v >>= fun effs ->
95+
data (effs @ CT.[set rd (var v)])
96+
97+
let (:=) = CT.set
98+
let (+) = CT.add
99+
100+
101+
let adc insn =
102+
require_gpr insn 1 @@ fun rd ->
103+
require_gpr insn 2 @@ fun rr ->
104+
with_result rd @@ fun r ->
105+
KB.return @@
106+
CT.[
107+
r := var rd + var rr + unsigned r8 (var cf);
108+
hf := halfcarry ~r ~rd ~rr;
109+
nf := nth (var r) 7;
110+
vf := overflow ~r ~rd ~rr;
111+
sf := var nf || var vf;
112+
zf := flag (is_zero (var r));
113+
cf := fullcarry ~r ~rd ~rr;
114+
]
115+
116+
117+
let add insn =
118+
require_gpr insn 1 @@ fun rd ->
119+
require_gpr insn 2 @@ fun rr ->
120+
with_result rd @@ fun r ->
121+
KB.return @@
122+
CT.[
123+
r := var rd + var rr;
124+
hf := halfcarry ~r ~rd ~rr;
125+
nf := nth (var r) 7;
126+
vf := overflow ~r ~rd ~rr;
127+
sf := var nf || var vf;
128+
zf := flag (is_zero (var r));
129+
cf := fullcarry ~r ~rd ~rr;
130+
]
131+
132+
let ldi insn =
133+
require_gpr insn 0 @@ fun rd ->
134+
require_imm insn 1 @@ fun k ->
135+
data [
136+
rd := const k
137+
]
138+
end
139+
140+
let lifter label : unit Theory.eff =
141+
KB.collect MC.Insn.slot label >>= function
142+
| None -> KB.return Insn.empty
143+
| Some insn ->
144+
Theory.instance () >>= Theory.require >>= fun (module Core) ->
145+
let module Avr = Avr(Core) in
146+
let open Avr in
147+
insn |> match MC.Insn.name insn with
148+
| "ADCRdRr" -> adc
149+
| "ADDRdRr" -> add
150+
| "LDIRdK" -> ldi
151+
| code ->
152+
info "unsupported opcode: %s" code;
153+
fun _ -> KB.return Insn.empty
154+
155+
let load () =
156+
KB.promise Theory.Semantics.slot lifter

plugins/avr/avr_lifter.mli

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val load : unit -> unit

plugins/avr/avr_main.ml

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
open Bap_main
2+
open Bap.Std
3+
open Bap_core_theory
4+
open KB.Syntax
5+
module CT = Theory
6+
7+
include Bap_main.Loggers()
8+
9+
module Target = Bap_avr_target
10+
module Dis = Disasm_expert.Basic
11+
12+
let provide_decoding () =
13+
KB.promise CT.Label.encoding @@ fun label ->
14+
CT.Label.target label >>| fun t ->
15+
if CT.Target.belongs Target.parent t
16+
then Target.llvm_avr16
17+
else CT.Language.unknown
18+
19+
let enable_llvm () =
20+
Dis.register Target.llvm_avr16 @@ fun _target ->
21+
Dis.create ~backend:"llvm" "avr"
22+
23+
let enable_loader () =
24+
let request_arch doc =
25+
let open Ogre.Syntax in
26+
match Ogre.eval (Ogre.request Image.Scheme.arch) doc with
27+
| Error _ -> assert false (* nothing could go wrong here! *)
28+
| Ok arch -> arch in
29+
KB.promise CT.Unit.target @@ fun unit ->
30+
KB.collect Image.Spec.slot unit >>| request_arch >>| function
31+
| Some "avr" -> Target.atmega328
32+
| _ -> CT.Target.unknown
33+
34+
35+
let main _ctxt =
36+
enable_llvm ();
37+
enable_loader ();
38+
provide_decoding ();
39+
Avr_lifter.load ();
40+
Ok ()
41+
42+
(* semantic tags that describe what our plugin is providing,
43+
setting them is important not only for introspection but
44+
for the proper function of the cache subsystem.
45+
*)
46+
let provides = [
47+
"avr";
48+
"lifter";
49+
]
50+
51+
(* finally, let's register our extension and call the main function *)
52+
let () = Bap_main.Extension.declare main
53+
~provides

0 commit comments

Comments
 (0)