Ⅰ. はじめに
ウィンドウを持たせずに、アイコンクリックで何かアクションを起こしたい場合に利用できます。
Ⅱ. サンプル
manifest.json
{ "background": { "scripts" : [ "js/background.js" ] } }
background.js
chrome.browserAction.onClicked.addListener(function () { console.log("icon clicked"); });
Ⅲ. 実行結果
ウィンドウを持たせずに、アイコンクリックで何かアクションを起こしたい場合に利用できます。
{ "background": { "scripts" : [ "js/background.js" ] } }
chrome.browserAction.onClicked.addListener(function () { console.log("icon clicked"); });
タイトルの通り「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 | 悪 |
私が調べた物の中で一番完成度が高い物でした。
復元が困難になります。
JavaScriptbeautifier を利用すると元の形に近いところまで復元されますが、完全な復元まで一手間がかかります。
JavaScriptbeautifier を利用すると元の形に近いところまで復元されます。
https://github.com/jacobsoo/Decoder-JJEncode
デコーダが存在します。復元が容易です。
Google がOSSとして公開しています。
圧縮や最適化がメインのようです。
難読化しても JavaScriptbeautifier を利用すると復元が容易です。
JavaScriptbeautifier を利用すると復元が容易です。
名前の通り圧縮がメインです。
難読化がメインではありません。
JavaScriptbeautifier を利用すると復元が容易です。
JavaScriptのコードがアスキーアートに変換されます。
aaencode の派生として (」・ω・)」うー!(/・ω・)/にゃー!encode があります。
JavaScriptbeautifier を利用しても元の形に戻りませんが、
aadecode を利用すると元の形に戻ります。
データ量が膨れ上がるので実用的ではありません。
JavaScriptを絵文字化するライブラリ。
webpackで使える。
試してないので難読化の強度は不明。
aaencode のようなもの。
https://github.com/PerimeterX/Restringer
https://www.perimeterx.com/tech-blog/2022/defeating-javascript-obfuscation/
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}"); }
C#側のDllImportの呼出規約はデフォルトでCallingConvention.StdCallになります。
C++側の関数の呼出規約はデフォルトで__cdeclです。
スタック処理が異なる為不具合が発生する場合があります。
明示的に指定する事を心がけたほうが良いです。
※呼出規約とは
https://kagasu.hatenablog.com/entry/2017/12/18/213255
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}"); } } }
Result = 3
私の知る限りバイナリから自動で.protoを生成するツールは公開されていません。
よって、シリアライズされているバイナリデータを読み取る為の.proto作成は手動となります。
また、ProtocolBuffersはシリアライズした時key-valueのkey情報が失われる為非常に手間がかかります。
インデックス番号のみ含まれています。
key情報があれば値が何の値を表しているかの情報として利用できる為分かりやすいですが、
key情報が無い為データ値を見て手探りする必要があります。
本記事では少しでも楽に.protoファイルを作る方法をまとめます。
Protocol Buffers の原理についてはこちら
https://developers.google.com/protocol-buffers/docs/encoding
また、この記事でサンプルとして使う元データは以下の通りです。
message Armor { uint64 id = 1; string name = 2; repeated Option attribute = 3; } message Option { uint32 id = 1; string detail = 2; }
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 } });
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) "木属性 耐性" } }
macOSでのみ動作します。(2017/03/17時点)
GUIがあるのでmacOSが使える状況であればコレが一番使いやすいです。
また、データが欠損している不完全なバイナリファイルでも解読が可能です。
(protoc --decode_rawしても「Filed to parse input.」と出るようなデータでも解析が可能)
こちらからダウンロードできます。
https://github.com/google/protobuf/releases
また、NuGetから Google.ProtocolBuffers パッケージをインストールすると protoc.exe も含まれています。
protoc.exe --decode_raw < binary_file > out.txt
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" } }
Diablo 3というゲームの.protoを自動生成する為に作られたプロジェクトのようです。
私の環境でDiablo 3ではない物を対象にして.protoの自動生成を試みましたが上手くいきませんでした。
https://github.com/fry/d3
説明省略
この記事では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