備忘録

備忘録

Chrome Extension 拡張機能 アイコンクリック時に何かする

Ⅰ. はじめに

ウィンドウを持たせずに、アイコンクリックで何かアクションを起こしたい場合に利用できます。

Ⅱ. サンプル

manifest.json
{
  "background": {
    "scripts" : [
      "js/background.js"
    ]
  }
}
background.js
chrome.browserAction.onClicked.addListener(function () {
  console.log("icon clicked");
});

Ⅲ. 実行結果

f:id:kagasu:20170402204528p:plain

JavaScript難読化ツールの紹介と比較

Ⅰ. はじめに

タイトルの通り「JavaScript難読化ツールの紹介と比較」です。
強力な難読化ツールは基本的に有料です。

※2017/07/28 追記
こだわる人用
ChromeやFireFoxの開発者ツールのブレークポイントを制限する方法 - 備忘録

※2022/05/29追記
deobfuscator
https://github.com/ben-sb/javascript-deobfuscator

Ⅱ. 比較(無料版)

# 名前 難読化
1 JavaScript obfuscator
2 </> Javascript Obfuscator
3 jjencode
4 Closure Compiler
5 /packer/
6 YUI Compressor
7 aaencode
8 emojify-webpack-plugin
9 JSFuck

1. JavaScript obfuscator

私が調べた物の中で一番完成度が高い物でした。
復元が困難になります。
JavaScriptbeautifier を利用すると元の形に近いところまで復元されますが、完全な復元まで一手間がかかります。

2. </> Javascript Obfuscator

JavaScriptbeautifier を利用すると元の形に近いところまで復元されます。

3. jjencode

https://github.com/jacobsoo/Decoder-JJEncode
デコーダが存在します。復元が容易です。

4. Closure Compiler

GoogleOSSとして公開しています。
圧縮や最適化がメインのようです。
難読化しても JavaScriptbeautifier を利用すると復元が容易です。

5. /packer/

JavaScriptbeautifier を利用すると復元が容易です。

6. YUI Compressor

名前の通り圧縮がメインです。
難読化がメインではありません。
JavaScriptbeautifier を利用すると復元が容易です。

7. aaencode

JavaScriptのコードがアスキーアートに変換されます。
aaencode の派生として (」・ω・)」うー!(/・ω・)/にゃー!encode があります。

JavaScriptbeautifier を利用しても元の形に戻りませんが、
aadecode を利用すると元の形に戻ります。
データ量が膨れ上がるので実用的ではありません。

8. emojify-webpack-plugin

JavaScriptを絵文字化するライブラリ。
webpackで使える。
試してないので難読化の強度は不明。

9. JSFuck

aaencode のようなもの。

有料版一覧

有料の為評価していません。

おまけ

SourceMapがある場合に利用できる復元ツール

C#(EXE)からC++(DLL)の関数を呼び出す方法

Ⅰ. はじめに

C#(EXE)からC++(DLL)の関数を呼び出す方法です。

方法が2つあります。

Ⅱ. 関数をエクスポートする方法(P/Invoke

1. サンプルプログラム

f:id:kagasu:20170327214624p:plain:h200

dllexport.def

EXPORTS 
  GetInt
  GetIntArray
  GetString
  GetStruct

MyDll.cpp

#define _CRT_SECURE_NO_WARNINGS

#include <Windows.h>

int GetInt()
{
  return 123;
}

void GetIntArray(int x[])
{
  x[0] = 1;
  x[1] = 2;
  x[2] = 3;
}

void GetString(char *str)
{
  strcpy(str, "あいうえお");
}

struct Data {
  int id;
  char name[512];
};

void GetStruct(Data *data)
{
  data->id = 1;
  strcpy(data->name, "名前1");
}

BOOL WINAPI DllMain(HINSTANCE hinstModule, DWORD dwReason, LPVOID lpvReserved)
{
  if (dwReason == DLL_PROCESS_ATTACH)
  {
    DisableThreadLibraryCalls(hinstModule);
  }

  return TRUE;
}

Program.cs

struct Data
{
  public int id;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
  public string name;
}

[DllImport("Dll.dll", CallingConvention = CallingConvention.StdCall)]
static extern int GetInt();

[DllImport("Dll.dll", CallingConvention = CallingConvention.StdCall)]
static extern void GetIntArray([Out, MarshalAs(UnmanagedType.LPArray)] int[] data);

[DllImport("Dll.dll", CallingConvention = CallingConvention.StdCall)]
static extern void GetString(StringBuilder str);

[DllImport("Dll.dll", CallingConvention = CallingConvention.StdCall)]
static extern void GetStruct(ref Data data);

static void Main(string[] args)
{
  // int
  Console.WriteLine(GetInt());

  // int配列
  var intArray = new int[3];
  GetIntArray(intArray);
  Console.WriteLine(string.Join(",", intArray));

  // string
  var sb = new StringBuilder();
  GetString(sb);
  Console.WriteLine(sb);

  // 構造体
  var data = new Data();
  GetStruct(ref data);
  Console.WriteLine($"{data.id},{data.name}");
}
2. 実行結果

f:id:kagasu:20170327221250p:plain

3. DllImportの呼出規約について

C#側のDllImportの呼出規約はデフォルトでCallingConvention.StdCallになります。
C++側の関数の呼出規約はデフォルトで__cdeclです。
スタック処理が異なる為不具合が発生する場合があります。
明示的に指定する事を心がけたほうが良いです。

※呼出規約とは
https://kagasu.hatenablog.com/entry/2017/12/18/213255

Ⅲ. C++/CLIを利用する方法

1. サンプルプログラム

f:id:kagasu:20210215234856p:plain:h200
f:id:kagasu:20210215235710p:plain:h200

Source.cpp

namespace TestDll
{
  public ref class MyClass
  {
  public:
    static int Sum(int a, int b)
    {
      return a + b;
    }
  };
}

Program.cs

using System;

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      var result = TestDll.MyClass.Sum(1, 2);
      Console.WriteLine($"Result = {result}");
    }
  }
}
2. 実行結果
Result = 3

.protoをシリアライズされているバイナリデータから作る

Ⅰ. はじめに

私の知る限りバイナリから自動で.protoを生成するツールは公開されていません。
よって、シリアライズされているバイナリデータを読み取る為の.proto作成は手動となります。

また、ProtocolBuffersはシリアライズした時key-valueのkey情報が失われる為非常に手間がかかります。
インデックス番号のみ含まれています。
key情報があれば値が何の値を表しているかの情報として利用できる為分かりやすいですが、
key情報が無い為データ値を見て手探りする必要があります。

本記事では少しでも楽に.protoファイルを作る方法をまとめます。

Protocol Buffers の原理についてはこちら
https://developers.google.com/protocol-buffers/docs/encoding

また、この記事でサンプルとして使う元データは以下の通りです。

armor.proto (proto3)

message Armor {
  uint64 id = 1;
  string name = 2;
  repeated Option attribute = 3;
}

message Option {
  uint32 id = 1;
  string detail = 2;
}

データ(C#)

var armors = new List<Armor>();

var fireOption = new Option() { Id = 1001, Detail = "火属性 耐性" };
var waterOption = new Option() { Id = 1002, Detail = "水属性 耐性" };
var woodOption = new Option() { Id = 1003, Detail = "木属性 耐性" };

armors.Add(new Armor() { Id = 100001, Name = "防具001", Attribute = new Option[] { fireOption } });
armors.Add(new Armor() { Id = 100002, Name = "防具002", Attribute = new Option[] { waterOption } });
armors.Add(new Armor() { Id = 100003, Name = "防具003", Attribute = new Option[] { waterOption, woodOption } });

Ⅱ. protofudger を使う方法

GO言語で書かれた非常に優秀なツールです。

ただし、文字列が 1024 * 32バイト(32,768バイト)を超えると例外を吐くので、
fork して修正したものも置いておきます。

使い方
protofudger.exe binary_file > out.txt
出力結果
1: {
  1: (varint) 100001
  2: (string) "防具001"
  3: {
    1: (varint) 1001
    2: (string) "火属性 耐性"
  }
}
1: {
  1: (varint) 100002
  2: (string) "防具002"
  3: {
    1: (varint) 1002
    2: (string) "水属性 耐性"
  }
}
1: {
  1: (varint) 100003
  2: (string) "防具003"
  3: {
    1: (varint) 1002
    2: (string) "水属性 耐性"
  }
  3: {
    1: (varint) 1003
    2: (string) "木属性 耐性"
  }
}

Ⅲ. Protobuf Viewerを使う方法


macOSでのみ動作します。(2017/03/17時点)
GUIがあるのでmacOSが使える状況であればコレが一番使いやすいです。
また、データが欠損している不完全なバイナリファイルでも解読が可能です。
(protoc --decode_rawしても「Filed to parse input.」と出るようなデータでも解析が可能)

https://sourceforge.net/projects/protobufviewer/

Ⅳ. protoc を使う方法

こちらからダウンロードできます。
https://github.com/google/protobuf/releases

また、NuGetから Google.ProtocolBuffers パッケージをインストールすると protoc.exe も含まれています。

使い方
protoc.exe --decode_raw < binary_file > out.txt
作った.protoをテストする場合
protoc --decode=PackageName.MessageName MessageName.proto < binary_file > out.txt
出力結果
1 {
  1: 100001
  2: "\351\230\262\345\205\267001"
  3 {
    1: 1001
    2: "\347\201\253\345\261\236\346\200\247 \350\200\220\346\200\247"
  }
}
1 {
  1: 100002
  2: "\351\230\262\345\205\267002"
  3 {
    1: 1002
    2: "\346\260\264\345\261\236\346\200\247 \350\200\220\346\200\247"
  }
}
1 {
  1: 100003
  2: "\351\230\262\345\205\267003"
  3 {
    1: 1002
    2: "\346\260\264\345\261\236\346\200\247 \350\200\220\346\200\247"
  }
  3 {
    1: 1003
    2: "\346\234\250\345\261\236\346\200\247 \350\200\220\346\200\247"
  }
}

Ⅴ. protobuf-inspectorを使う

説明省略
https://github.com/mildsunrise/protobuf-inspector

Ⅵ. d3を使う

Diablo 3というゲームの.protoを自動生成する為に作られたプロジェクトのようです。
私の環境でDiablo 3ではない物を対象にして.protoの自動生成を試みましたが上手くいきませんでした。
https://github.com/fry/d3

Androidで複数キーでソートする

Ⅰ. はじめに

この記事ではStream APIを利用したソートとLightweight-Stream-APIを利用したソートの2つを紹介します。
Java8で追加されたStream APIとComparatorを使ったソートはAndroid N (Android 7.0)以上でなければ動作しません。
Android N 以下の場合はlambdaで例外が発生しアプリが強制終了します。

Ⅱ. 使うライブラリ

Lightweight-Stream-API を使います。
Android 4.4.4で動作確認済みです。

略称はLSAだそうです。(ライブラリ作者がReadmeでLSAと書いている)

LSAはStream APIではできない書き方ができるので便利です。

例えば、以下のようにメソッドの評価結果でソートが可能です。

persons = persons
       .sorted(theComparing(comparing(x -> x.getSomething(argSomething))))
       .toList();

ラムダ式を使ったこの書き方はStream APIでも1つだけのソートであれば可能ですが、
複数キーでソートする場合は不可能です。
なんとも中途半端な作りですね・・・

// OK
persons.sort(comparing(x -> x.name));
// OK
persons.sort(comparing(x -> x.getName()));
// NG
persons.sort(comparing(x -> x.name).thenComparing(x -> x.age));
// NG
persons.sort(comparing(x -> x.getName()).thenComparing(x -> x.getAge()));

また、ixJavaなど他のライブラリもありましたが、複数キーでソートするにはLSAが最適でした。
(ixJavaはそもそも複数キーソートに対応していない)

Ⅲ. プログラム

import java.util.ArrayList;
import java.util.List;

public class Main {

  static class Person {

    public Person(String name, Integer age){
      this.name = name;
      this.age = age;
    }

    private String name;
    public String getName() {
      return name;
    }

    private Integer age;
    public Integer getAge() {
      return age;
    }
  }

  public static void main(String[] args) {

    List<Person> persons = new ArrayList<>();

    persons.add(new Person("a", 1));
    persons.add(new Person("b", 4));
    persons.add(new Person("b", 2));
    persons.add(new Person("c", 3));

    // Stream API
    // Android 7.0未満では動きません
    // import static java.util.Comparator.*;
    // persons.sort(comparing(Person::getName, reverseOrder()).thenComparing(Person::getAge));

    // Lightweight-Stream-API
    // Android 4.4.4で動作確認済み
    // import static com.annimon.stream.ComparatorCompat.*;
    persons = Stream.of(persons)
           .sorted(thenComparing(comparing(x -> x.name, reverseOrder()), comparing(x -> x.age)))
           .toList();

    // または
    //persons = Stream.of(persons)
    //       .sorted(thenComparing(comparing(Person::getName, reverseOrder()), comparing(Person::getAge)))
    //       .toList();

    for(Person p : persons) {
      System.out.println(p.name + p.age);
    }

  }
}

Ⅳ. 出力結果

c3
b2
b4
a1