out-of-tree kernel {module, exploit} development tool
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. // Copyright 2018 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package qemu
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "math/rand"
  11. "net"
  12. "os"
  13. "os/exec"
  14. "runtime"
  15. "strings"
  16. "syscall"
  17. "time"
  18. "golang.org/x/crypto/ssh"
  19. )
  20. func readUntilEOF(pipe io.ReadCloser, buf *[]byte) (err error) {
  21. bufSize := 1024
  22. for err != io.EOF {
  23. stdout := make([]byte, bufSize)
  24. var n int
  25. n, err = pipe.Read(stdout)
  26. if err != nil && err != io.EOF {
  27. return
  28. }
  29. *buf = append(*buf, stdout[:n]...)
  30. }
  31. if err == io.EOF {
  32. err = nil
  33. }
  34. return
  35. }
  36. type arch string
  37. const (
  38. // X86x64 is the qemu-system-x86_64
  39. X86x64 arch = "x86_64"
  40. // X86x32 is the qemu-system-i386
  41. X86x32 = "i386"
  42. // TODO add other
  43. unsupported = "unsupported" // for test purposes
  44. )
  45. // Kernel describe kernel parameters for qemu
  46. type Kernel struct {
  47. Name string
  48. KernelPath string
  49. InitrdPath string
  50. }
  51. // System describe qemu parameters and executed process
  52. type System struct {
  53. arch arch
  54. kernel Kernel
  55. drivePath string
  56. Cpus int
  57. Memory int
  58. debug bool
  59. gdb string // tcp::1234
  60. noKASLR bool
  61. noSMEP bool
  62. noSMAP bool
  63. noKPTI bool
  64. // Timeout works after Start invocation
  65. Timeout time.Duration
  66. KilledByTimeout bool
  67. KernelPanic bool
  68. Died bool
  69. sshAddrPort string
  70. // accessible while qemu is running
  71. cmd *exec.Cmd
  72. pipe struct {
  73. stdin io.WriteCloser
  74. stderr io.ReadCloser
  75. stdout io.ReadCloser
  76. }
  77. Stdout, Stderr []byte
  78. // accessible after qemu is closed
  79. exitErr error
  80. }
  81. // NewSystem constructor
  82. func NewSystem(arch arch, kernel Kernel, drivePath string) (q *System, err error) {
  83. if _, err = exec.LookPath("qemu-system-" + string(arch)); err != nil {
  84. return
  85. }
  86. q = &System{}
  87. q.arch = arch
  88. if _, err = os.Stat(kernel.KernelPath); err != nil {
  89. return
  90. }
  91. q.kernel = kernel
  92. if _, err = os.Stat(drivePath); err != nil {
  93. return
  94. }
  95. q.drivePath = drivePath
  96. // Default values
  97. q.Cpus = 1
  98. q.Memory = 512 // megabytes
  99. return
  100. }
  101. func getRandomAddrPort() (addr string) {
  102. // 127.1-255.0-255.0-255:10000-50000
  103. ip := fmt.Sprintf("127.%d.%d.%d",
  104. rand.Int()%254+1, rand.Int()%255, rand.Int()%254)
  105. port := rand.Int()%40000 + 10000
  106. return fmt.Sprintf("%s:%d", ip, port)
  107. }
  108. func getRandomPort(ip string) (addr string) {
  109. // ip:1024-65535
  110. port := rand.Int()%(65536-1024) + 1024
  111. return fmt.Sprintf("%s:%d", ip, port)
  112. }
  113. func getFreeAddrPort() (addrPort string) {
  114. timeout := time.Now().Add(time.Second)
  115. for {
  116. if runtime.GOOS == "linux" {
  117. addrPort = getRandomAddrPort()
  118. } else {
  119. addrPort = getRandomPort("127.0.0.1")
  120. }
  121. ln, err := net.Listen("tcp", addrPort)
  122. if err == nil {
  123. ln.Close()
  124. return
  125. }
  126. if time.Now().After(timeout) {
  127. panic("Can't found free address:port on localhost")
  128. }
  129. }
  130. }
  131. func kvmExists() bool {
  132. if _, err := os.Stat("/dev/kvm"); err != nil {
  133. return false
  134. }
  135. file, err := os.OpenFile("/dev/kvm", os.O_WRONLY, 0666)
  136. if err != nil {
  137. if os.IsPermission(err) {
  138. return false
  139. }
  140. }
  141. file.Close()
  142. return true
  143. }
  144. func (q *System) panicWatcher() {
  145. for {
  146. time.Sleep(time.Second)
  147. if bytes.Contains(q.Stdout, []byte("Kernel panic")) {
  148. time.Sleep(time.Second)
  149. // There is no reason to stay alive after kernel panic
  150. q.Stop()
  151. q.KernelPanic = true
  152. return
  153. }
  154. }
  155. }
  156. func (q System) cmdline() (s string) {
  157. s = "root=/dev/sda ignore_loglevel console=ttyS0 rw"
  158. if q.noKASLR {
  159. s += " nokaslr"
  160. }
  161. if q.noSMEP {
  162. s += " nosmep"
  163. }
  164. if q.noSMAP {
  165. s += " nosmap"
  166. }
  167. if q.noKPTI {
  168. s += " nokpti"
  169. }
  170. return
  171. }
  172. // Start qemu process
  173. func (q *System) Start() (err error) {
  174. rand.Seed(time.Now().UnixNano()) // Are you sure?
  175. q.sshAddrPort = getFreeAddrPort()
  176. hostfwd := fmt.Sprintf("hostfwd=tcp:%s-:22", q.sshAddrPort)
  177. qemuArgs := []string{"-snapshot", "-nographic",
  178. "-hda", q.drivePath,
  179. "-kernel", q.kernel.KernelPath,
  180. "-smp", fmt.Sprintf("%d", q.Cpus),
  181. "-m", fmt.Sprintf("%d", q.Memory),
  182. "-device", "e1000,netdev=n1",
  183. "-netdev", "user,id=n1," + hostfwd,
  184. }
  185. if q.debug {
  186. qemuArgs = append(qemuArgs, "-gdb", q.gdb)
  187. }
  188. if q.kernel.InitrdPath != "" {
  189. qemuArgs = append(qemuArgs, "-initrd", q.kernel.InitrdPath)
  190. }
  191. if (q.arch == X86x64 || q.arch == X86x32) && kvmExists() {
  192. qemuArgs = append(qemuArgs, "-enable-kvm", "-cpu", "host")
  193. }
  194. if q.arch == X86x64 && runtime.GOOS == "darwin" {
  195. qemuArgs = append(qemuArgs, "-accel", "hvf", "-cpu", "host")
  196. }
  197. qemuArgs = append(qemuArgs, "-append", q.cmdline())
  198. q.cmd = exec.Command("qemu-system-"+string(q.arch), qemuArgs...)
  199. if q.pipe.stdin, err = q.cmd.StdinPipe(); err != nil {
  200. return
  201. }
  202. if q.pipe.stdout, err = q.cmd.StdoutPipe(); err != nil {
  203. return
  204. }
  205. if q.pipe.stderr, err = q.cmd.StderrPipe(); err != nil {
  206. return
  207. }
  208. err = q.cmd.Start()
  209. if err != nil {
  210. return
  211. }
  212. go readUntilEOF(q.pipe.stdout, &q.Stdout)
  213. go readUntilEOF(q.pipe.stderr, &q.Stderr)
  214. go func() {
  215. q.exitErr = q.cmd.Wait()
  216. q.Died = true
  217. }()
  218. time.Sleep(time.Second / 10) // wait for immediately die
  219. if q.Died {
  220. err = errors.New("qemu died immediately: " + string(q.Stderr))
  221. }
  222. go q.panicWatcher()
  223. if q.Timeout != 0 {
  224. go func() {
  225. time.Sleep(q.Timeout)
  226. q.KilledByTimeout = true
  227. q.Stop()
  228. }()
  229. }
  230. return
  231. }
  232. // Stop qemu process
  233. func (q *System) Stop() {
  234. // 1 00/01 01 01 SOH (Ctrl-A) START OF HEADING
  235. fmt.Fprintf(q.pipe.stdin, "%cx", 1)
  236. // wait for die
  237. time.Sleep(time.Second / 10)
  238. if !q.Died {
  239. q.cmd.Process.Signal(syscall.SIGTERM)
  240. time.Sleep(time.Second / 10)
  241. q.cmd.Process.Signal(syscall.SIGKILL)
  242. }
  243. }
  244. func (q System) ssh(user string) (client *ssh.Client, err error) {
  245. cfg := &ssh.ClientConfig{
  246. User: user,
  247. HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  248. }
  249. client, err = ssh.Dial("tcp", q.sshAddrPort, cfg)
  250. return
  251. }
  252. // Command executes shell commands on qemu system
  253. func (q System) Command(user, cmd string) (output string, err error) {
  254. client, err := q.ssh(user)
  255. if err != nil {
  256. return
  257. }
  258. defer client.Close()
  259. session, err := client.NewSession()
  260. if err != nil {
  261. return
  262. }
  263. bytesOutput, err := session.CombinedOutput(cmd)
  264. output = string(bytesOutput)
  265. return
  266. }
  267. // AsyncCommand executes command on qemu system but does not wait for exit
  268. func (q System) AsyncCommand(user, cmd string) (err error) {
  269. client, err := q.ssh(user)
  270. if err != nil {
  271. return
  272. }
  273. defer client.Close()
  274. session, err := client.NewSession()
  275. if err != nil {
  276. return
  277. }
  278. return session.Run(fmt.Sprintf(
  279. "nohup sh -c '%s' > /dev/null 2> /dev/null < /dev/null &", cmd))
  280. }
  281. // CopyFile is copy file from local machine to remote through ssh/scp
  282. func (q *System) CopyFile(user, localPath, remotePath string) (err error) {
  283. addrPort := strings.Split(q.sshAddrPort, ":")
  284. addr := addrPort[0]
  285. port := addrPort[1]
  286. cmd := exec.Command("scp", "-P", port,
  287. "-o", "StrictHostKeyChecking=no",
  288. "-o", "LogLevel=error",
  289. localPath, user+"@"+addr+":"+remotePath)
  290. output, err := cmd.CombinedOutput()
  291. if err != nil {
  292. return errors.New(string(output))
  293. }
  294. return
  295. }
  296. // CopyAndInsmod copy kernel module to temporary file on qemu then insmod it
  297. func (q *System) CopyAndInsmod(localKoPath string) (output string, err error) {
  298. remoteKoPath := fmt.Sprintf("/tmp/module_%d.ko", rand.Int())
  299. err = q.CopyFile("root", localKoPath, remoteKoPath)
  300. if err != nil {
  301. return
  302. }
  303. return q.Command("root", "insmod "+remoteKoPath)
  304. }
  305. // CopyAndRun is copy local file to qemu vm then run it
  306. func (q *System) CopyAndRun(user, path string) (output string, err error) {
  307. remotePath := fmt.Sprintf("/tmp/executable_%d", rand.Int())
  308. err = q.CopyFile(user, path, remotePath)
  309. if err != nil {
  310. return
  311. }
  312. return q.Command(user, "chmod +x "+remotePath+" && "+remotePath)
  313. }
  314. // Debug is for enable qemu debug and set hostname and port for listen
  315. func (q *System) Debug(conn string) {
  316. q.debug = true
  317. q.gdb = conn
  318. }
  319. // SetKASLR is changing KASLR state through kernel boot args
  320. func (q *System) SetKASLR(state bool) {
  321. q.noKASLR = !state
  322. }
  323. // SetSMEP is changing SMEP state through kernel boot args
  324. func (q *System) SetSMEP(state bool) {
  325. q.noSMEP = !state
  326. }
  327. // SetSMAP is changing SMAP state through kernel boot args
  328. func (q *System) SetSMAP(state bool) {
  329. q.noSMAP = !state
  330. }
  331. // SetKPTI is changing KPTI state through kernel boot args
  332. func (q *System) SetKPTI(state bool) {
  333. q.noKPTI = !state
  334. }
  335. // GetKASLR is retrieve KASLR settings
  336. func (q *System) GetKASLR() bool {
  337. return !q.noKASLR
  338. }
  339. // GetSMEP is retrieve SMEP settings
  340. func (q *System) GetSMEP() bool {
  341. return !q.noSMEP
  342. }
  343. // GetSMAP is retrieve SMAP settings
  344. func (q *System) GetSMAP() bool {
  345. return !q.noSMAP
  346. }
  347. // GetKPTI is retrieve KPTI settings
  348. func (q *System) GetKPTI() bool {
  349. return !q.noKPTI
  350. }
  351. // GetSSHCommand returns command for connect to qemu machine over ssh
  352. func (q System) GetSSHCommand() (cmd string) {
  353. addrPort := strings.Split(q.sshAddrPort, ":")
  354. addr := addrPort[0]
  355. port := addrPort[1]
  356. cmd = "ssh -o StrictHostKeyChecking=no"
  357. cmd += " -p " + port + " root@" + addr
  358. return
  359. }