Compare commits
781
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c69564297 | ||
|
|
0c6852b392 | ||
|
|
f085589957 | ||
|
|
d6d1cd2485 | ||
|
|
af7a439633 | ||
|
|
81b70bc358 | ||
|
|
5eb0049a9f | ||
|
|
b7b4333703 | ||
|
|
0696bde266 | ||
|
|
88640f60fe | ||
|
|
636b245f1b | ||
|
|
b71a591a60 | ||
|
|
2f02eff988 | ||
|
|
5510b27d7f | ||
|
|
475a0f84da | ||
|
|
63bf91405c | ||
|
|
2a7746998d | ||
|
|
abc4f47683 | ||
|
|
c8544b1574 | ||
|
|
4578647c59 | ||
|
|
cc39fc53b8 | ||
|
|
13c3b68730 | ||
|
|
18a1e8cd0e | ||
|
|
933fcb33c1 | ||
|
|
f6fe575094 | ||
|
|
8342e3a78f | ||
|
|
bfaa5a6c2c | ||
|
|
b189c3ceb1 | ||
|
|
bcc8a773e2 | ||
|
|
6cc817e8bd | ||
|
|
9426c3f1bb | ||
|
|
5691606aa9 | ||
|
|
45e3edc17f | ||
|
|
04aef6df33 | ||
|
|
552634d634 | ||
|
|
0e779533a9 | ||
|
|
c5c75c38c3 | ||
|
|
569d2bc6b9 | ||
|
|
889fb374df | ||
|
|
24a3aa08d1 | ||
|
|
00d8e44cbf | ||
|
|
3fb9ffaa4d | ||
|
|
db0afce5f3 | ||
|
|
5612496f6d | ||
|
|
fe5291e916 | ||
|
|
d4a598e936 | ||
|
|
ddaa18301a | ||
|
|
d6832c3632 | ||
|
|
9b6950373c | ||
|
|
6020df53d9 | ||
|
|
a6fdf426bd | ||
|
|
db177c91b8 | ||
|
|
d186ca7e41 | ||
|
|
aa5750c263 | ||
|
|
8c5e34cd37 | ||
|
|
0270dd5739 | ||
|
|
0a14bbbf05 | ||
|
|
f6f5c16097 | ||
|
|
f4c99ab8a5 | ||
|
|
954badbaaa | ||
|
|
22958558ea | ||
|
|
fef38e4d22 | ||
|
|
136d4aab18 | ||
|
|
68cb02fe8b | ||
|
|
8c5088c8cd | ||
|
|
3d328193d6 | ||
|
|
f0da05f67f | ||
|
|
01174c4a81 | ||
|
|
6489d58851 | ||
|
|
59c91c88fe | ||
|
|
c89b3a00dc | ||
|
|
170d0bf9e2 | ||
|
|
6935dc7fe1 | ||
|
|
04a516b1e3 | ||
|
|
4b38d18995 | ||
|
|
c3f169823f | ||
|
|
063de5014f | ||
|
|
e6905c18e0 | ||
|
|
9405bd660d | ||
|
|
27a9ceade7 | ||
|
|
c399670653 | ||
|
|
863674b2e9 | ||
|
|
c2d8146f94 | ||
|
|
c94f06d98f | ||
|
|
5b769d3a78 | ||
|
|
6987cb066a | ||
|
|
7e9c91199e | ||
|
|
9db1fd0bd7 | ||
|
|
796b65e1cd | ||
|
|
beca9977eb | ||
|
|
6051cb2c0c | ||
|
|
864160d885 | ||
|
|
db675f1d52 | ||
|
|
911152a04d | ||
|
|
0fec65d06b | ||
|
|
cf415619ca | ||
|
|
cfb49eb8a8 | ||
|
|
8998438c23 | ||
|
|
c3acc34475 | ||
|
|
761881cec3 | ||
|
|
b79d379c25 | ||
|
|
6fbdbd6820 | ||
|
|
53568568bf | ||
|
|
f36c10f9e8 | ||
|
|
d39a450bdf | ||
|
|
98b07d07dd | ||
|
|
f198993f41 | ||
|
|
54c0ed42aa | ||
|
|
0a0244db42 | ||
|
|
9f5dc52be8 | ||
|
|
42c276af1e | ||
|
|
291bc6fb70 | ||
|
|
facaae865a | ||
|
|
d0ff57a45c | ||
|
|
0b04d384cc | ||
|
|
e6abaefbbc | ||
|
|
f5539cfc2f | ||
|
|
2a620c06cd | ||
|
|
e750c262fb | ||
|
|
c717204795 | ||
|
|
e1afbc1914 | ||
|
|
a503376b2f | ||
|
|
4fb2f3e6ed | ||
|
|
d8e314cb24 | ||
|
|
19c52f5e3f | ||
|
|
b8312fa8d1 | ||
|
|
fd975c75fa | ||
|
|
e9c48a61e4 | ||
|
|
b4927a0a4f | ||
|
|
5cc841d5c1 | ||
|
|
11b9f0346c | ||
|
|
cf77d073fc | ||
|
|
de5bfec79f | ||
|
|
37025cc09f | ||
|
|
0b32577a90 | ||
|
|
eacf8461da | ||
|
|
b34bdcdb28 | ||
|
|
de43da71ae | ||
|
|
6a1e558db3 | ||
|
|
8e74c9c62f | ||
|
|
44d5b4b25e | ||
|
|
637bcb5e41 | ||
|
|
33d5ae0b52 | ||
|
|
24a30fd91c | ||
|
|
b620e2a2bd | ||
|
|
58a91f03d7 | ||
|
|
5160d9b717 | ||
|
|
089bbf58f3 | ||
|
|
72201668ed | ||
|
|
84094af54a | ||
|
|
16624f1324 | ||
|
|
9664b2b101 | ||
|
|
2609352eef | ||
|
|
90f532feb1 | ||
|
|
e9ebfb94ef | ||
|
|
13935077ba | ||
|
|
ae9aaee16b | ||
|
|
cdddfddf49 | ||
|
|
441813c070 | ||
|
|
c12bad7f13 | ||
|
|
70e5c24718 | ||
|
|
283c82f68c | ||
|
|
c29d85a7db | ||
|
|
b7d803dd10 | ||
|
|
5ac3dc96f0 | ||
|
|
96af2fa744 | ||
|
|
67756dacc0 | ||
|
|
80c3d36dfa | ||
|
|
c715af8058 | ||
|
|
55c9ce26d1 | ||
|
|
a69b8337ab | ||
|
|
1d9b96a9c4 | ||
|
|
3e598b2d38 | ||
|
|
1f79002497 | ||
|
|
d84bc8f11a | ||
|
|
65a809a0da | ||
|
|
4034883e98 | ||
|
|
1d7115b7b4 | ||
|
|
c1d0002470 | ||
|
|
e8939ca220 | ||
|
|
329cf206d1 | ||
|
|
2b27515476 | ||
|
|
44fd18305f | ||
|
|
6c13fc8e0a | ||
|
|
70e8a8f860 | ||
|
|
7a7460f04f | ||
|
|
b3f1b4e822 | ||
|
|
480b16b9c1 | ||
|
|
e762e32afb | ||
|
|
f7366a517f | ||
|
|
faf209e50d | ||
|
|
0a0174b9ac | ||
|
|
e1df5e8555 | ||
|
|
d575a7340b | ||
|
|
6495171812 | ||
|
|
14b19a0b8a | ||
|
|
9fd65ce071 | ||
|
|
567a82f8d4 | ||
|
|
82626c4499 | ||
|
|
ae06dcf83f | ||
|
|
08561e0eb7 | ||
|
|
0639a541a3 | ||
|
|
826583dd43 | ||
|
|
c3136064c7 | ||
|
|
2a4979b7cd | ||
|
|
a61491ad00 | ||
|
|
2847311a57 | ||
|
|
4458128a84 | ||
|
|
34f7c4fd66 | ||
|
|
67252b23b9 | ||
|
|
02d0702761 | ||
|
|
e714fb027a | ||
|
|
f983c718c3 | ||
|
|
3d450a29fc | ||
|
|
73b09f4c5c | ||
|
|
dbcf8c3391 | ||
|
|
f2e9c491ea | ||
|
|
4a45c14742 | ||
|
|
2b86ea9c7c | ||
|
|
f201b04fbb | ||
|
|
246170238e | ||
|
|
0a49d5bfb9 | ||
|
|
458771cb58 | ||
|
|
7d7b3f961a | ||
|
|
35f1ddd6bc | ||
|
|
b2d5bc1109 | ||
|
|
704fa7699c | ||
|
|
2f59287f1a | ||
|
|
d83f0983d5 | ||
|
|
1136ea8810 | ||
|
|
e44cb45908 | ||
|
|
8d39eae6b9 | ||
|
|
09d7b9bab5 | ||
|
|
9a70fb482c | ||
|
|
f591e37a1e | ||
|
|
a0784c0d2c | ||
|
|
96d3cafb07 | ||
|
|
ee043e2cbf | ||
|
|
3eb7dbdc16 | ||
|
|
90b06db6c0 | ||
|
|
6b92002597 | ||
|
|
bc4aa46d7c | ||
|
|
549effa056 | ||
|
|
dcae3c6c14 | ||
|
|
84a17d5519 | ||
|
|
017297c40c | ||
|
|
ffd9611308 | ||
|
|
81ae838567 | ||
|
|
d1c904bac7 | ||
|
|
8f9da25164 | ||
|
|
fe48e779c2 | ||
|
|
9a5b077899 | ||
|
|
9588482bfe | ||
|
|
0e0b386dc3 | ||
|
|
a1b7d40bb0 | ||
|
|
5945915f91 | ||
|
|
bef234d6ce | ||
|
|
64717ad7da | ||
|
|
d830cb79b3 | ||
|
|
f2e22e13a6 | ||
|
|
b743bd20a0 | ||
|
|
ec6db0f063 | ||
|
|
1f5be750bc | ||
|
|
e2fc2493b6 | ||
|
|
be783efaba | ||
|
|
7005166f58 | ||
|
|
2f71375efc | ||
|
|
34cc9259c1 | ||
|
|
8b08ec0af9 | ||
|
|
82560ebf59 | ||
|
|
8a4c4b657f | ||
|
|
1d0baa352f | ||
|
|
7c323870c2 | ||
|
|
d6cc9ca3a6 | ||
|
|
fe297e56e8 | ||
|
|
1e63144390 | ||
|
|
cd756b07d5 | ||
|
|
7914e36ab9 | ||
|
|
3b2ec69c54 | ||
|
|
4bb056bbb5 | ||
|
|
c789af04b7 | ||
|
|
acb3e0f2e5 | ||
|
|
3db46813f8 | ||
|
|
cc0ee2edc6 | ||
|
|
f8e143a3bc | ||
|
|
ae0630a619 | ||
|
|
81b5104276 | ||
|
|
4a1837d9d5 | ||
|
|
770d2f60aa | ||
|
|
eb89a16d9b | ||
|
|
bfb7776006 | ||
|
|
4b1066501b | ||
|
|
8b01e0cc89 | ||
|
|
915855e75c | ||
|
|
488f448ae2 | ||
|
|
15f92f2988 | ||
|
|
16f4609d68 | ||
|
|
eaf5734083 | ||
|
|
135d102f9d | ||
|
|
d4c2c3c18f | ||
|
|
17809caacd | ||
|
|
0136ee8c22 | ||
|
|
65a6288257 | ||
|
|
99d31ee24a | ||
|
|
3c157b4ec6 | ||
|
|
c1b370ce0d | ||
|
|
5a67c7fc54 | ||
|
|
b5c2954ad6 | ||
|
|
6218d289e2 | ||
|
|
23862af1b5 | ||
|
|
e94be95c66 | ||
|
|
83defc054b | ||
|
|
eb69763d27 | ||
|
|
289aef6838 | ||
|
|
a1ed4232de | ||
|
|
c701376777 | ||
|
|
2af12a18a3 | ||
|
|
5e1afe454f | ||
|
|
a9e00b83f3 | ||
|
|
5f93b4b125 | ||
|
|
46d195e8a4 | ||
|
|
9833c94656 | ||
|
|
6e6ff7bd2e | ||
|
|
9455e7c8bc | ||
|
|
e6134056cf | ||
|
|
b49beb42d7 | ||
|
|
a4d77f0ff0 | ||
|
|
82a1a525d7 | ||
|
|
e8b92a40c0 | ||
|
|
d52f66d62a | ||
|
|
e2f03786f4 | ||
|
|
5a55d5b1c6 | ||
|
|
ef30343154 | ||
|
|
38bbb656da | ||
|
|
89252f1f35 | ||
|
|
5547c1e697 | ||
|
|
cf96ceb211 | ||
|
|
a079af297b | ||
|
|
79e5a3923d | ||
|
|
25fb6d32a3 | ||
|
|
40e18b2e37 | ||
|
|
bfbfc7330a | ||
|
|
3dcb732da3 | ||
|
|
6a7650e93c | ||
|
|
170600541b | ||
|
|
68b9fa489b | ||
|
|
8a31508c89 | ||
|
|
f5009ed55f | ||
|
|
60825fb447 | ||
|
|
5dfd9905ed | ||
|
|
6fee42689a | ||
|
|
5ccb9241be | ||
|
|
163ee09a74 | ||
|
|
b9fb438835 | ||
|
|
7af7bc88ce | ||
|
|
c5d165bd03 | ||
|
|
78d594cacd | ||
|
|
3362dfa31e | ||
|
|
6fa6300987 | ||
|
|
4fc6565b0f | ||
|
|
8d5c88f17c | ||
|
|
1d13cef5f4 | ||
|
|
1154f2f03c | ||
|
|
2a7dcb77c1 | ||
|
|
5acdb9dc6f | ||
|
|
53409ab196 | ||
|
|
03424ae91e | ||
|
|
6b55de017b | ||
|
|
7425f70fd0 | ||
|
|
28f10cf2bc | ||
|
|
2bf3663f65 | ||
|
|
6e4535c101 | ||
|
|
c6dad93208 | ||
|
|
63cf28fd8f | ||
|
|
63ba7424c0 | ||
|
|
28d5c397c8 | ||
|
|
aafdf8a01b | ||
|
|
f8a53a8c06 | ||
|
|
d88d6c1897 | ||
|
|
9c3479ed95 | ||
|
|
26829045d0 | ||
|
|
1fd1989d34 | ||
|
|
f517a5a1d7 | ||
|
|
c93422a898 | ||
|
|
984fc27ce1 | ||
|
|
0e25befe78 | ||
|
|
ed725dace4 | ||
|
|
7b18c58140 | ||
|
|
51956c1a49 | ||
|
|
93731e17dd | ||
|
|
6a278a68dd | ||
|
|
76e7e510bb | ||
|
|
966cca8efe | ||
|
|
1d62e54e1f | ||
|
|
b33236ec08 | ||
|
|
96a46ada0a | ||
|
|
b4fc0ecaa1 | ||
|
|
64c71880ae | ||
|
|
fe64fd65d9 | ||
|
|
ae0f725fad | ||
|
|
a6132be054 | ||
|
|
fc58fa1eb0 | ||
|
|
cbc0dd1e7c | ||
|
|
eb8f6491a8 | ||
|
|
6aa9afb549 | ||
|
|
49887f0239 | ||
|
|
5b172b1042 | ||
|
|
06b047a092 | ||
|
|
161ed91571 | ||
|
|
6f0ef16050 | ||
|
|
bda4ada7fb | ||
|
|
35a4e18d30 | ||
|
|
c67530535a | ||
|
|
701efd5f90 | ||
|
|
e45da24297 | ||
|
|
4a6073810a | ||
|
|
fc4a9a0592 | ||
|
|
abd7a5ed09 | ||
|
|
58381eefa2 | ||
|
|
ebb065e2dc | ||
|
|
2cedde9f1c | ||
|
|
248972f327 | ||
|
|
5047373006 | ||
|
|
7dc40d3d9e | ||
|
|
dca14900c4 | ||
|
|
93aa9eb5c2 | ||
|
|
d76f7d1508 | ||
|
|
9f496ef2d4 | ||
|
|
5065fcd520 | ||
|
|
0a4caf4a0a | ||
|
|
98b2b027af | ||
|
|
8aaf9a1978 | ||
|
|
d5c43d65f0 | ||
|
|
a45fe86a39 | ||
|
|
71ac83f3dd | ||
|
|
fb3749aff6 | ||
|
|
f26c8b5b8c | ||
|
|
6fc77206e4 | ||
|
|
90b25f2e30 | ||
|
|
ea4db4c9f7 | ||
|
|
a11c6bd5e4 | ||
|
|
3d889dc934 | ||
|
|
f8d84da127 | ||
|
|
33d4761cc7 | ||
|
|
b5afa5f91e | ||
|
|
a0f5e33bd8 | ||
|
|
002503d0c0 | ||
|
|
9a487a4e00 | ||
|
|
e5c6fccc35 | ||
|
|
9837203234 | ||
|
|
ef1a0cc65a | ||
|
|
a6ba772c08 | ||
|
|
5e334a0e12 | ||
|
|
9049620dfe | ||
|
|
a65755728f | ||
|
|
7acf4e741a | ||
|
|
c7530f9a81 | ||
|
|
ef245c0bd0 | ||
|
|
19f01e2610 | ||
|
|
6d74992efe | ||
|
|
3e24a72465 | ||
|
|
dda8e6a728 | ||
|
|
2366bda37b | ||
|
|
e0d76ae80d | ||
|
|
f14b6a8430 | ||
|
|
118d43e787 | ||
|
|
c92a344d47 | ||
|
|
ac3bae3cb9 | ||
|
|
348c65c7ec | ||
|
|
9e39795895 | ||
|
|
91280393cd | ||
|
|
2e81e867b2 | ||
|
|
810fe53e79 | ||
|
|
28713f9b4d | ||
|
|
2ccc86cd13 | ||
|
|
dcde958450 | ||
|
|
e6ba9c3af3 | ||
|
|
a0ec3e2016 | ||
|
|
37d2ebac4c | ||
|
|
63800ff5a2 | ||
|
|
45fcd28b40 | ||
|
|
9226ade511 | ||
|
|
aa8082ea6c | ||
|
|
b3f013fa18 | ||
|
|
2ed89e6529 | ||
|
|
88c940b165 | ||
|
|
d93ebec9df | ||
|
|
71e5cb1640 | ||
|
|
fd552a8c4b | ||
|
|
be121d87eb | ||
|
|
4f25ee33dc | ||
|
|
b9292516f4 | ||
|
|
f826aa893f | ||
|
|
11b7f918c2 | ||
|
|
55249704c5 | ||
|
|
b956bd589f | ||
|
|
62634e4c01 | ||
|
|
28fd49f0f6 | ||
|
|
4f5110306d | ||
|
|
9a15525298 | ||
|
|
27c272bd9a | ||
|
|
dd3e7aef0d | ||
|
|
b72c079a9a | ||
|
|
1d769cd261 | ||
|
|
071cfd1bb6 | ||
|
|
f0a68dce60 | ||
|
|
2db668f6ca | ||
|
|
7a9f587c3a | ||
|
|
900d7c8606 | ||
|
|
62b37111e7 | ||
|
|
5574287101 | ||
|
|
5b5b76a583 | ||
|
|
4d2faef37b | ||
|
|
96a9f32dc1 | ||
|
|
f6a58f1062 | ||
|
|
d5f28fb29c | ||
|
|
15ab5dbcaf | ||
|
|
4e3953cc45 | ||
|
|
8b243877c2 | ||
|
|
418a5562f6 | ||
|
|
7714e763d9 | ||
|
|
df7151ba30 | ||
|
|
53bfa7e3f7 | ||
|
|
e52fe9719f | ||
|
|
d421d3f286 | ||
|
|
aeebba391e | ||
|
|
c78fe6990b | ||
|
|
eab34a9d8c | ||
|
|
73370e1c06 | ||
|
|
c60b737f7d | ||
|
|
b08228d19f | ||
|
|
767c6c9a95 | ||
|
|
f430c416c7 | ||
|
|
4801a24089 | ||
|
|
5afd8a850b | ||
|
|
9f48a76e11 | ||
|
|
2a4667bb79 | ||
|
|
0804722cdf | ||
|
|
898b0f62d7 | ||
|
|
6729ee7636 | ||
|
|
e5ba6934e4 | ||
|
|
faaee693d5 | ||
|
|
05f43ed388 | ||
|
|
76b70203d9 | ||
|
|
b808e2d6bb | ||
|
|
4037c6545d | ||
|
|
d52af3b321 | ||
|
|
d98dcfa8c2 | ||
|
|
dd114d3980 | ||
|
|
ab985c6a10 | ||
|
|
e32028d1b5 | ||
|
|
01be082e9e | ||
|
|
a08be51e9f | ||
|
|
507e8b3c7b | ||
|
|
e3e7664665 | ||
|
|
c371d6e1e9 | ||
|
|
14f9b3d931 | ||
|
|
37fe105ba1 | ||
|
|
9075ce71ad | ||
|
|
00f707b250 | ||
|
|
ae849a7281 | ||
|
|
5738a0cf8c | ||
|
|
895079dd09 | ||
|
|
eb68761eb0 | ||
|
|
602d0021d4 | ||
|
|
0674facd46 | ||
|
|
8e8921d40c | ||
|
|
63e0822cc7 | ||
|
|
cfd2b0c75d | ||
|
|
1be370543f | ||
|
|
dd90965058 | ||
|
|
77cf757103 | ||
|
|
021ba690b0 | ||
|
|
01779a6f61 | ||
|
|
35c82b93eb | ||
|
|
6c88094da4 | ||
|
|
e0fd8207a1 | ||
|
|
da0970672d | ||
|
|
676959ecb5 | ||
|
|
b7df5b7805 | ||
|
|
4aa2b60c1d | ||
|
|
4e17be5705 | ||
|
|
bb19c9477e | ||
|
|
f608127635 | ||
|
|
957069d14d | ||
|
|
93091abb4d | ||
|
|
c57f1b74e0 | ||
|
|
3427593daf | ||
|
|
bddeb634b5 | ||
|
|
3b92a2bced | ||
|
|
8b9940a7b5 | ||
|
|
b6d9acca63 | ||
|
|
2d4a910fcd | ||
|
|
35e5508fd0 | ||
|
|
69d8afefc1 | ||
|
|
c55ae3b9ec | ||
|
|
166dcd21d0 | ||
|
|
c182f72ad3 | ||
|
|
6cc45e8dc9 | ||
|
|
e20da1f205 | ||
|
|
4a6c17ce83 | ||
|
|
c9f49db907 | ||
|
|
b912b117f0 | ||
|
|
f72931d9dd | ||
|
|
5cbf06c7ad | ||
|
|
11f27deddf | ||
|
|
bef4321afb | ||
|
|
3f1a77f68e | ||
|
|
880cca1bca | ||
|
|
f85056d83e | ||
|
|
527b76ef78 | ||
|
|
ce73c3fed2 | ||
|
|
430c297ca4 | ||
|
|
c3f2b8e774 | ||
|
|
bc2cd8b874 | ||
|
|
63012eeb60 | ||
|
|
5fd0db37d9 | ||
|
|
e7cbfe04fd | ||
|
|
522c2d0ecb | ||
|
|
5ead2cfbc4 | ||
|
|
3a4a63f70a | ||
|
|
58ef8ac3aa | ||
|
|
be3cfbffdf | ||
|
|
11fc3b9815 | ||
|
|
78f4848d70 | ||
|
|
c13c9d41f8 | ||
|
|
ad1f6b508f | ||
|
|
6aa6350c80 | ||
|
|
f7954a8fd6 | ||
|
|
de9593ad48 | ||
|
|
1260cacc67 | ||
|
|
ef535b81fe | ||
|
|
64ebdf4501 | ||
|
|
6dd719eaf6 | ||
|
|
3ed59b2812 | ||
|
|
764ff05231 | ||
|
|
75f50d86f6 | ||
|
|
6caa38a0ca | ||
|
|
0374865836 | ||
|
|
e9fac56232 | ||
|
|
5a0e0ba3b8 | ||
|
|
3af3811ab0 | ||
|
|
2572e1eb2f | ||
|
|
24a0bf9a61 | ||
|
|
630bdafcac | ||
|
|
dd3640548f | ||
|
|
e84ad86a75 | ||
|
|
b3b44d8a2d | ||
|
|
02beb74535 | ||
|
|
21a8bd0ccd | ||
|
|
78014e3d14 | ||
|
|
472b72a530 | ||
|
|
894c30ef02 | ||
|
|
f9eaf056ef | ||
|
|
50787b2faf | ||
|
|
911d580634 | ||
|
|
b62931f833 | ||
|
|
d2aed172ab | ||
|
|
00a6ac3de9 | ||
|
|
4bfd8038be | ||
|
|
e1ca412757 | ||
|
|
66c26f280b | ||
|
|
cffc548cd3 | ||
|
|
ae81f64d2a | ||
|
|
c945931961 | ||
|
|
69be514c67 | ||
|
|
554e9b1612 | ||
|
|
9622b4b4cf | ||
|
|
8238232485 | ||
|
|
c8e66c704a | ||
|
|
91daaa68ae | ||
|
|
9a4e6da5a9 | ||
|
|
fb679b63c2 | ||
|
|
6d1f39b250 | ||
|
|
99263a720d | ||
|
|
5af1699103 | ||
|
|
d3f498ceec | ||
|
|
1d15bcb9ec | ||
|
|
739039092f | ||
|
|
3f6d6dc756 | ||
|
|
5a7c769692 | ||
|
|
8d7071da4c | ||
|
|
72548712fe | ||
|
|
904d03e987 | ||
|
|
d0f3480a3c | ||
|
|
00eb03d0f2 | ||
|
|
75a75cbb20 | ||
|
|
09c249deb8 | ||
|
|
a5b970c6bf | ||
|
|
65c879757e | ||
|
|
18c18290d0 | ||
|
|
b1b687fb75 | ||
|
|
863ed47de2 | ||
|
|
1dccd0dee0 | ||
|
|
6582b2433a | ||
|
|
66f3cfa594 | ||
|
|
e2c23f3640 | ||
|
|
8d44b3edd3 | ||
|
|
431e8ebfcf | ||
|
|
9dd6cef90a | ||
|
|
9a2f395028 | ||
|
|
fd008cd8e0 | ||
|
|
fe843ec0b1 | ||
|
|
38ebd54776 | ||
|
|
3201f0538d | ||
|
|
af48fe8791 | ||
|
|
417737748a | ||
|
|
98de3380e3 | ||
|
|
9bab55337a | ||
|
|
c3bc38bf6c | ||
|
|
7a96391001 | ||
|
|
ab5a1c61b1 | ||
|
|
f04445c57c | ||
|
|
44b3627ab0 | ||
|
|
26881da76a | ||
|
|
d311940f24 | ||
|
|
cf061159ab | ||
|
|
5bcb6645b5 | ||
|
|
fd3282a87e | ||
|
|
a4e57366dd | ||
|
|
d565ecdc25 | ||
|
|
8189081511 | ||
|
|
7f1fa64019 | ||
|
|
c8aed4740d | ||
|
|
fa02df9ad3 | ||
|
|
ddea06360a | ||
|
|
94cc8475e0 | ||
|
|
1a24be574d | ||
|
|
217e8cf59f | ||
|
|
a55b5bb8ee | ||
|
|
ba35b5509d | ||
|
|
11d0ef2e09 | ||
|
|
805c338f36 | ||
|
|
a6a8f67649 | ||
|
|
7864d665e1 | ||
|
|
df6e476d67 | ||
|
|
ed3a7f070c | ||
|
|
132a3a46be | ||
|
|
e828acce0e | ||
|
|
53fa55937f | ||
|
|
e60aa11108 | ||
|
|
8b228ae701 | ||
|
|
03bc6d36db | ||
|
|
b6c21b1db5 | ||
|
|
433ebda8b4 | ||
|
|
2c1bf92010 | ||
|
|
7d4ed59816 | ||
|
|
7f1ae42f71 | ||
|
|
6ad62cec6a | ||
|
|
39db9fe424 | ||
|
|
c3d013b2fd | ||
|
|
a5d2986b1e | ||
|
|
6a37346acb | ||
|
|
f94fc7967b | ||
|
|
88a3c3f8a4 | ||
|
|
8dd1d84471 | ||
|
|
5802ef8746 | ||
|
|
3aaf7e2c54 | ||
|
|
fc80ea88df | ||
|
|
b89de8feab | ||
|
|
ae72abd019 | ||
|
|
ea0b733687 | ||
|
|
19f8cddfd6 | ||
|
|
9f42495d11 | ||
|
|
9b392bb2f8 | ||
|
|
5f27e02fae | ||
|
|
735c10059c | ||
|
|
77799e6c90 | ||
|
|
965d0b35f9 | ||
|
|
5295d56b10 | ||
|
|
dee8853fde | ||
|
|
494d4db6d9 | ||
|
|
e7f54b3310 | ||
|
|
3c84080aab | ||
|
|
55e5eb090b | ||
|
|
5e7f35c7c3 | ||
|
|
9c10a64f87 | ||
|
|
ba3869c597 | ||
|
|
38b293329c | ||
|
|
feeb19b8b3 | ||
|
|
2ef02ced16 |
@@ -28,7 +28,7 @@ intentionally not included. -->
|
||||
- Provider, bridge, target-format, or platform compatibility:
|
||||
- Evidence, artifact, provenance, or reconstruction contract:
|
||||
- Process execution, authorization, cleanup, or containment impact:
|
||||
- Generated metadata (`docs/product-catalog.json`), package, or installation impact:
|
||||
- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `skill-src/` and never commit build outputs):
|
||||
|
||||
## Evidence and regression coverage
|
||||
|
||||
@@ -59,12 +59,18 @@ relevant real-provider or platform workflow, or explain why it was unavailable.
|
||||
limits, security/privacy/containment implications, and meaningful proof gaps. -->
|
||||
|
||||
- Breaking changes or migration steps:
|
||||
<!-- For ! / BREAKING CHANGE: show a previously valid call/configuration,
|
||||
its new behavior and migration, and why compatibility/deprecation cannot
|
||||
preserve it. Every release increments minor, including breaking changes;
|
||||
keep breaking markers and migration notes so callers can assess compatibility.
|
||||
See docs/releasing.md. -->
|
||||
- Real Hopper/Ghidra, browser, or OS coverage:
|
||||
- Package or release metadata impact:
|
||||
- Security, privacy, process, or containment review:
|
||||
|
||||
## Review checklist
|
||||
|
||||
- [ ] This PR addresses a concrete problem or an agreed enhancement.
|
||||
- [ ] The PR has one focused outcome and the title follows `type(scope): outcome`.
|
||||
- [ ] Related issue is linked, or the reason for not linking one is stated above.
|
||||
- [ ] Tests cover changed observable behavior and meaningful failure paths.
|
||||
|
||||
+161
-22
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, "release/*"]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -77,6 +77,8 @@ jobs:
|
||||
- run: npm ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run check:ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run verify:test-discovery
|
||||
- if: github.event_name == 'pull_request' && needs.changes.outputs.code != 'true'
|
||||
run: npm run format:check
|
||||
|
||||
@@ -100,12 +102,18 @@ jobs:
|
||||
with:
|
||||
name: rea-dist
|
||||
path: dist
|
||||
- name: Generate test artifacts from the downloaded runtime
|
||||
run: |
|
||||
node scripts/generate-mcp-tool-catalog.mjs
|
||||
node scripts/generate-skill-metadata.mjs
|
||||
node scripts/generate-product-catalog.mjs
|
||||
node scripts/generate-completion-ledger.mjs
|
||||
- run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: vitest-report-${{ strategy.job-index }}
|
||||
path: .vitest-reports/*
|
||||
path: .vitest/blob/*
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
overwrite: true
|
||||
@@ -157,7 +165,7 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
with:
|
||||
path: .vitest-reports
|
||||
path: .vitest/blob
|
||||
pattern: vitest-report-*
|
||||
merge-multiple: true
|
||||
- name: Merge coverage, JUnit, and timing reports
|
||||
@@ -215,29 +223,93 @@ jobs:
|
||||
- name: Verify fixtures from a clean checkout
|
||||
run: npm run verify:fixtures
|
||||
|
||||
package-e2e:
|
||||
name: Package/install integration (${{ matrix.os }})
|
||||
windows-native-build:
|
||||
name: Build bundled Windows x64 native controls
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: |
|
||||
# Slow Azure archive transfers can exhaust the job before compilation.
|
||||
# Keep the same signed packages and avoid slow HTTP mirror fallback.
|
||||
for source in /etc/apt/apt-mirrors.txt /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
|
||||
if [ -f "$source" ]; then
|
||||
sudo sed -i 's|azure.archive.ubuntu.com|archive.ubuntu.com|g; s|http://archive.ubuntu.com|https://archive.ubuntu.com|g' "$source"
|
||||
fi
|
||||
done
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
package-e2e:
|
||||
name: Native package baseline (${{ matrix.platform }} ${{ matrix.architecture }})
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-14]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: macos-15
|
||||
platform: darwin
|
||||
architecture: arm64
|
||||
- os: macos-15-intel
|
||||
platform: darwin
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:package
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
if: matrix.platform == 'darwin'
|
||||
run: npm run verify:inspector
|
||||
|
||||
apple-artifacts:
|
||||
name: Real Apple artifact CLI/MCP E2E
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -248,8 +320,21 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run verify:keyed-archive
|
||||
- run: npm run verify:asset-catalog
|
||||
- run: npm run verify:macos-bundle
|
||||
- name: Native installer and compiled NIB hierarchy regressions
|
||||
run: npm run test:focused -- tests/boundary/filesystem/installScript.test.ts tests/boundary/filesystem/nibNativeHierarchy.test.ts tests/boundary/filesystem/interfaceBuilderBundle.test.ts
|
||||
- name: Real binary metadata and Interface Builder integration
|
||||
run: npm run verify:apple-dispatch && npm run verify:interface-builder
|
||||
- name: Native plist CLI selection regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
|
||||
- name: Native Swift demangling CLI and MCP regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativeDemangleCli.test.ts tests/boundary/mcp/nativeDemangleMcp.test.ts
|
||||
- name: Native signature target binding and CLI/MCP recovery regressions
|
||||
run: npm run test:focused -- tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts tests/boundary/cli/nativeSignatureCli.test.ts tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts
|
||||
- name: Process filesystem snapshot descriptor regressions
|
||||
run: npm run test:focused -- src/process/capture/FilesystemSnapshot.test.ts
|
||||
- name: Native Darwin ownership and process cleanup regressions
|
||||
run: npm run test:focused -- tests/boundary/process/darwinProcessRunTokenReader.test.ts tests/boundary/process/directAnalysisSignals.test.ts tests/boundary/process/processCaptureLifecycle.test.ts tests/boundary/process/processCli.test.ts tests/boundary/mcp/sessionMcp.test.ts
|
||||
|
||||
reconstruction-readiness:
|
||||
name: Reconstruction readiness
|
||||
@@ -286,23 +371,45 @@ jobs:
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: windows-latest
|
||||
platform: win32
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
run: npm run verify:inspector
|
||||
|
||||
windows-curated:
|
||||
name: Windows capability and package lane
|
||||
needs: changes
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 25
|
||||
@@ -315,6 +422,11 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run build:cached
|
||||
- run: npm run typecheck
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- name: Record named Windows host capabilities
|
||||
run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json
|
||||
- name: Validate Windows host capability report
|
||||
@@ -327,26 +439,33 @@ jobs:
|
||||
# ignores unmatched paths, so a stale entry would otherwise report green
|
||||
# while skipping coverage.
|
||||
#
|
||||
# Every path here must pass on windows-latest WITHOUT verified Windows
|
||||
# native authority. src/ghidra/GhidraProvider.test.ts,
|
||||
# src/ghidra/GhidraProvider.behavior.test.ts successors, and
|
||||
# tests/boundary/process/providerProcess.test.ts are deliberately absent:
|
||||
# they require Job Object process ownership and private-runtime DACL
|
||||
# enforcement, which this runner does not provide by design. Those claims
|
||||
# belong behind the same approval boundary as the real Ghidra Windows
|
||||
# workflow, not in the default lane.
|
||||
# The matching native artifact establishes real Job Object and private
|
||||
# runtime boundaries for these fixtures. Ghidra and Java are not needed;
|
||||
# real engine claims remain in the separate controlled-provider workflow.
|
||||
- name: Run curated Windows capability tests
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
tests/boundary/filesystem/binaryTarget.test.ts
|
||||
tests/boundary/filesystem/safeOutputTree.test.ts
|
||||
tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts
|
||||
tests/boundary/filesystem/setupUnavailableProvider.test.ts
|
||||
tests/boundary/filesystem/windowsNpxRegistration.test.ts
|
||||
tests/boundary/filesystem/webScriptManifestPaths.test.ts
|
||||
tests/boundary/process/ProcessEvidence.host.test.ts
|
||||
tests/acceptance/webModuleCli.test.ts
|
||||
tests/acceptance/webSourceLocationCli.test.ts
|
||||
tests/boundary/cli/referenceSourceImportStatus.test.ts
|
||||
src/cliReferenceSourceImportStatus.test.ts
|
||||
tests/boundary/filesystem/asarInventoryFreshness.test.ts
|
||||
tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
|
||||
tests/conformance/providers/ghidra/windowsCapabilities.test.ts
|
||||
tests/conformance/providers/ghidra/ghidraWindowsFixture.test.ts
|
||||
src/ghidra/GhidraInstallation.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTransport.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraLauncher.test.ts
|
||||
src/windows/WindowsNativeLoader.test.ts
|
||||
src/windows/WindowsOwnedProcess.test.ts
|
||||
src/ghidra/GhidraLauncherEnvironment.test.ts
|
||||
src/process/ProcessOwnership.test.ts
|
||||
src/process/ProcessOwnership.part2.test.ts
|
||||
src/process/ProcessOwnership.part3.test.ts
|
||||
@@ -354,7 +473,23 @@ jobs:
|
||||
src/application/Update.test.ts
|
||||
tests/process-global/updateMaintenance.test.ts
|
||||
tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts
|
||||
- run: npm run verify:windows-native
|
||||
- run: npm run verify:package:windows
|
||||
- name: Verify native directory identity on Node 24
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
tests/boundary/filesystem/directoryArtifactReaderOpen.test.ts
|
||||
tests/boundary/filesystem/javascriptPackagePrecedence.test.ts
|
||||
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.19.0
|
||||
- name: Verify native directory identity on Node 22
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
tests/boundary/filesystem/directoryArtifactReaderOpen.test.ts
|
||||
tests/boundary/filesystem/javascriptPackagePrecedence.test.ts
|
||||
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
@@ -374,9 +509,13 @@ jobs:
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:check
|
||||
- run: npm run docs:api:cached
|
||||
- name: Generation leaves authored files unchanged
|
||||
run: git diff --exit-code
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: build/api-docs
|
||||
retention-days: 14
|
||||
name: generated-docs
|
||||
path: |
|
||||
docs/public/product-catalog.json
|
||||
docs/verification/managed-conformance-*.json
|
||||
skills/reverse-engineer-anything
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Source-built NativeAOT fixtures
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "bridge/ghidra/extensions/nativeaot/**"
|
||||
- "scripts/generate-nativeaot-fixtures.mjs"
|
||||
- "tests/fixtures/nativeaot/**"
|
||||
- ".github/workflows/nativeaot-fixtures.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
windows-fixture:
|
||||
name: Build Windows x64 NativeAOT fixture
|
||||
# .NET 8.0.22's bundled linker discovery supports VS 2019/2022.
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
|
||||
DOTNET_PROCESSOR_COUNT: "1"
|
||||
COMPlus_GCHeapHardLimit: "30000000"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "8.0.416"
|
||||
- name: Build benign fixture and independent byte oracle
|
||||
run: node scripts/generate-nativeaot-fixtures.mjs
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nativeaot-win-x64-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: |
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols/NativeAotFixture.exe
|
||||
_reference/nativeaot-integration/generated/win-x64/NativeAotFixture.map
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols.txt
|
||||
_reference/nativeaot-integration/generated/win-x64/oracle.json
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Documentation build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: documentation-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:build
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Real offline binary layout
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
large_mcp:
|
||||
description: "Also verify the complete 65,281-section result over MCP (slow)"
|
||||
type: boolean
|
||||
default: false
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-binary-layout.yml"
|
||||
- "bridge/pwntools/**"
|
||||
- "src/native/pwntools/**"
|
||||
- "src/domain/native/binaryLayout*"
|
||||
- "src/application/binaryDiagnostics/**"
|
||||
- "src/contracts/native/binaryDiagnosticsToolContracts.ts"
|
||||
- "src/composition/binaryDiagnostics.ts"
|
||||
- "src/server/registerBinaryDiagnosticsTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/binaryDiagnosticsCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "scripts/verify-binary-layout.mjs"
|
||||
- "scripts/lib/elf-section-name-fixtures.mjs"
|
||||
- "scripts/lib/large-result-mcp.mjs"
|
||||
- "scripts/fixtures/binary-layout*"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-binary-layout-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
REA_VERIFY_LARGE_ELF_MCP: ${{ inputs.large_mcp && '1' || '0' }}
|
||||
|
||||
jobs:
|
||||
real-binary-layout:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Acquire exact upstream libraries in an isolated runner environment
|
||||
shell: bash
|
||||
run: |
|
||||
layout_python_root="${RUNNER_TEMP}/rea-pwntools-layout"
|
||||
python3 -m venv "${layout_python_root}"
|
||||
"${layout_python_root}/bin/python" -m pip install --disable-pip-version-check \
|
||||
'pwntools==4.15.0' 'pyelftools==0.33' 'unicorn==2.1.2'
|
||||
command -v gcc
|
||||
command -v ld
|
||||
command -v strip
|
||||
command -v strace
|
||||
echo "REA_PWNTOOLS_PYTHON=${layout_python_root}/bin/python" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned fixtures through public CLI and MCP
|
||||
run: npm run verify:binary:layout
|
||||
@@ -5,10 +5,34 @@ on:
|
||||
paths:
|
||||
- ".github/workflows/real-browser.yml"
|
||||
- "scripts/verify-real-browser.mjs"
|
||||
- "scripts/verify-browser-network.mjs"
|
||||
- "scripts/verify-browser-scripts.mjs"
|
||||
- "scripts/verify-browser-modules.mjs"
|
||||
- "scripts/verify-browser-dom-destinations.mjs"
|
||||
- "scripts/lib/browser-*.mjs"
|
||||
- "scripts/fixtures/browser-network-site.mjs"
|
||||
- "scripts/fixtures/browser-script-site.mjs"
|
||||
- "scripts/fixtures/browser-module-site.mjs"
|
||||
- "scripts/fixtures/browser-verifier-site.mjs"
|
||||
- "scripts/fixtures/page-cdp-proxy.mjs"
|
||||
- "src/browser/**"
|
||||
- "src/domain/browser*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/domain/webModule*.ts"
|
||||
- "src/application/WebModule*.ts"
|
||||
- "src/composition/webModules.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "src/artifacts/ArtifactPaths.ts"
|
||||
- "src/application/WebScriptExportService.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/server/registerWebScriptTool.ts"
|
||||
- "src/server/registerWebModuleTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webModuleCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/cli/webScriptCommands.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Real offline EVM interface
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-evm-interface.yml"
|
||||
- "src/evm/**"
|
||||
- "src/domain/evm/**"
|
||||
- "src/application/evm/**"
|
||||
- "src/contracts/evm/**"
|
||||
- "src/composition/evm.ts"
|
||||
- "src/cli/evmCommands.ts"
|
||||
- "src/server/registerEvmTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "scripts/verify-evm-interface.mjs"
|
||||
- "scripts/fixtures/evm-interface.sol"
|
||||
- "third_party/evmole/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-evm-interface-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
|
||||
jobs:
|
||||
real-evm-interface:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Acquire unchanged fixture compiler in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
evm_fixture_root="${RUNNER_TEMP}/rea-evm-fixture-compiler"
|
||||
mkdir -p "${evm_fixture_root}"
|
||||
npm install --prefix "${evm_fixture_root}" --ignore-scripts --no-audit --no-fund --save-exact solc@0.8.30
|
||||
command -v prlimit
|
||||
command -v strace
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_SOLC_MODULE=${evm_fixture_root}/node_modules/solc/index.js" >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned compiled bytecode through public CLI and MCP
|
||||
run: npm run verify:evm:interface
|
||||
@@ -11,9 +11,36 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
native-artifact:
|
||||
name: Build matching Windows native artifact
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: |
|
||||
for source in /etc/apt/apt-mirrors.txt /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
|
||||
if [ -f "$source" ]; then
|
||||
sudo sed -i 's|azure.archive.ubuntu.com|archive.ubuntu.com|g; s|http://archive.ubuntu.com|https://archive.ubuntu.com|g' "$source"
|
||||
fi
|
||||
done
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
verify:
|
||||
name: Real Ghidra 12.1.4 Windows x64 PE proof
|
||||
if: github.ref == 'refs/heads/main'
|
||||
needs: native-artifact
|
||||
environment: real-ghidra-windows
|
||||
runs-on: [self-hosted, Windows, x64, ghidra-12-1-2]
|
||||
timeout-minutes: 20
|
||||
@@ -23,13 +50,35 @@ jobs:
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:windows-native
|
||||
- name: Verify all admitted Windows Ghidra operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows | Tee-Object -FilePath windows-ghidra-proof.log
|
||||
- name: Verify native x86 Windows Ghidra operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows -- --x86 | Tee-Object -FilePath windows-ghidra-x86-proof.log
|
||||
- name: Verify ordinary-user CLI and MCP operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows:package | Tee-Object -FilePath windows-ghidra-package-proof.log
|
||||
- name: Verify ordinary-user x86 CLI and MCP operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows:package -- --x86 | Tee-Object -FilePath windows-ghidra-x86-package-proof.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: windows-ghidra-proof
|
||||
path: windows-ghidra-proof.log
|
||||
path: |
|
||||
windows-ghidra-proof.log
|
||||
windows-ghidra-package-proof.log
|
||||
windows-ghidra-x86-proof.log
|
||||
windows-ghidra-x86-package-proof.log
|
||||
retention-days: 30
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Real JavaScript recovery
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-javascript-recovery.yml"
|
||||
- "src/javascript/recovery/**"
|
||||
- "src/domain/javascript/**"
|
||||
- "src/application/javascript/JavaScriptRecovery*.ts"
|
||||
- "src/composition/javascriptRecovery.ts"
|
||||
- "src/contracts/javascript/javascriptRecoveryToolContracts.ts"
|
||||
- "src/cli/javascriptRecoveryCommands.ts"
|
||||
- "src/cli/javascriptApplicationAnalysis.ts"
|
||||
- "src/server/registerJavaScriptRecoveryTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/application/CapabilityInventory.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "scripts/rea.mjs"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/SafeOutputTree.ts"
|
||||
- "scripts/verify/javascript/recovery.mjs"
|
||||
- "scripts/fixtures/javascript-recovery/**"
|
||||
- "third_party/wakaru"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: javascript-recovery-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: "--max-old-space-size=768"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
|
||||
jobs:
|
||||
real-javascript-recovery:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Fetch pinned Wakaru release into the job workspace
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/rea-wakaru"
|
||||
curl --fail --location --retry 3 https://github.com/pionxzh/wakaru/releases/download/v1.13.0/wakaru-linux-x64.tar.gz -o "$RUNNER_TEMP/rea-wakaru/release.tar.gz"
|
||||
tar -xzf "$RUNNER_TEMP/rea-wakaru/release.tar.gz" -C "$RUNNER_TEMP/rea-wakaru" wakaru
|
||||
printf '%s %s\n' e8c7ca052974604197389f6ace7464e4dd24dce64e2730971521bd6606ec9c9e "$RUNNER_TEMP/rea-wakaru/wakaru" | sha256sum --check
|
||||
- name: Install isolated fixture compilers
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-fixture-compilers" --ignore-scripts --no-audit --no-fund esbuild@0.25.10 webpack@5.101.3
|
||||
- run: npm run build:cached
|
||||
- run: npm run verify:javascript:recovery
|
||||
env:
|
||||
REA_WAKARU_COMMAND: ${{ runner.temp }}/rea-wakaru/wakaru
|
||||
REA_JAVASCRIPT_FIXTURE_TOOLS: ${{ runner.temp }}/rea-fixture-compilers
|
||||
@@ -0,0 +1,99 @@
|
||||
name: Real recorded crash evidence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-recorded-crash.yml"
|
||||
- "bridge/pwntools/**"
|
||||
- "bridge/pwndbg/**"
|
||||
- "src/native/pwntools/**"
|
||||
- "src/native/pwndbg/**"
|
||||
- "src/domain/native/recordedCrash*"
|
||||
- "src/application/binaryDiagnostics/**"
|
||||
- "src/contracts/native/binaryDiagnosticsToolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/contracts/toolOutputSchemaGroups.ts"
|
||||
- "src/composition/binaryDiagnostics.ts"
|
||||
- "src/server/registerRecordedCrashTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/cli/binaryDiagnosticsCommands.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "scripts/verify-recorded-crash.mjs"
|
||||
- "scripts/verify/recorded-crash/**"
|
||||
- "scripts/lib/recorded-core-fixtures.py"
|
||||
- "scripts/fixtures/recorded-crash.c"
|
||||
- "third_party/pwndbg/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-recorded-crash-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
UV_CONCURRENT_BUILDS: "1"
|
||||
UV_CONCURRENT_INSTALLS: "1"
|
||||
UV_PYTHON_DOWNLOADS: never
|
||||
|
||||
jobs:
|
||||
real-recorded-crash:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install GDB on the disposable CI runner
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install --yes --no-install-recommends gdb
|
||||
- name: Acquire exact unchanged upstreams in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
command -v gcc
|
||||
command -v gdb
|
||||
command -v strace
|
||||
core_decoder_root="${RUNNER_TEMP}/rea-core-decoder"
|
||||
core_pwndbg_root="${RUNNER_TEMP}/rea-core-pwndbg"
|
||||
core_pwndbg_venv="${RUNNER_TEMP}/rea-core-pwndbg-venv"
|
||||
python3 -m venv "${core_decoder_root}"
|
||||
"${core_decoder_root}/bin/python" -m pip install --disable-pip-version-check \
|
||||
'pwntools==4.15.0' 'pyelftools==0.33' 'unicorn==2.1.2' 'uv==0.12.0'
|
||||
git init "${core_pwndbg_root}"
|
||||
git -C "${core_pwndbg_root}" remote add origin https://github.com/pwndbg/pwndbg.git
|
||||
git -C "${core_pwndbg_root}" fetch --depth=1 origin 55596aa676a2756efb8d67034f31675170eabe48
|
||||
git -C "${core_pwndbg_root}" checkout --detach FETCH_HEAD
|
||||
test "$(git -C "${core_pwndbg_root}" rev-parse HEAD)" = 55596aa676a2756efb8d67034f31675170eabe48
|
||||
UV_PROJECT_ENVIRONMENT="${core_pwndbg_venv}" \
|
||||
"${core_decoder_root}/bin/uv" sync --project "${core_pwndbg_root}" \
|
||||
--frozen --no-editable --no-default-groups --python /usr/bin/python3 --no-managed-python
|
||||
test -z "$(git -C "${core_pwndbg_root}" status --porcelain --untracked-files=no)"
|
||||
echo "REA_PWNTOOLS_PYTHON=${core_decoder_root}/bin/python" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_GDB=$(command -v gdb)" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_GDBINIT=${core_pwndbg_root}/gdbinit.py" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_VENV_PATH=${core_pwndbg_venv}" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
- name: Build and install the exact package in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
npm run build:cached
|
||||
npm pack --ignore-scripts --json --pack-destination "${RUNNER_TEMP}" > "${RUNNER_TEMP}/rea-core-pack.json"
|
||||
core_package_name=$(node -p 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"))[0].filename' "${RUNNER_TEMP}/rea-core-pack.json")
|
||||
npm install --prefix "${RUNNER_TEMP}/rea-core-installed" --ignore-scripts --no-fund --no-audit "${RUNNER_TEMP}/${core_package_name}"
|
||||
- name: Verify installed CLI and MCP against the source-owned recording
|
||||
shell: bash
|
||||
run: node scripts/verify-recorded-crash.mjs "${RUNNER_TEMP}/rea-core-installed/node_modules/rea-agents/scripts/rea.mjs"
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Real historical web network captures
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-network-captures.yml"
|
||||
- "bridge/mitmproxy/**"
|
||||
- "src/browser/history/**"
|
||||
- "src/domain/webNetworkCapture*.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "src/contracts/errorSchemas.ts"
|
||||
- "src/domain/explicitSensitive*.ts"
|
||||
- "src/application/WebNetworkCapture*"
|
||||
- "src/composition/webNetworkCaptures.ts"
|
||||
- "src/cli/webNetworkCaptureCommands.ts"
|
||||
- "src/server/registerWebNetworkCaptureTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/toolRegistrationOptions.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/process/capture/ProcessCaptureLifecycle.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "src/contracts/webNetworkCaptureToolContracts.ts"
|
||||
- "scripts/verify-web-network-captures.mjs"
|
||||
- "scripts/fixtures/generate-mitmproxy-capture.py"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-web-network-captures-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
|
||||
jobs:
|
||||
real-web-network-captures:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Use pinned package manager
|
||||
run: npm install --global npm@11.16.0
|
||||
- run: npm ci
|
||||
- name: Acquire fixed upstream native profile in runner temporary storage
|
||||
shell: bash
|
||||
run: |
|
||||
capture_tool_root="${RUNNER_TEMP}/rea-mitmproxy-12.2.3"
|
||||
mkdir -p "${capture_tool_root}"
|
||||
curl --fail --location --retry 3 --max-time 180 \
|
||||
https://downloads.mitmproxy.org/12.2.3/mitmproxy-12.2.3-linux-x86_64.tar.gz \
|
||||
--output "${capture_tool_root}/mitmproxy.tar.gz"
|
||||
echo '2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5 mitmproxy.tar.gz' \
|
||||
| (cd "${capture_tool_root}" && sha256sum --check --strict)
|
||||
tar -xzf "${capture_tool_root}/mitmproxy.tar.gz" -C "${capture_tool_root}"
|
||||
echo "REA_MITMDUMP_COMMAND=${capture_tool_root}/mitmdump" >> "${GITHUB_ENV}"
|
||||
- name: Verify upstream producer fixtures through public CLI and MCP
|
||||
run: npm run verify:web:network-captures
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Real website runtime attribution
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-runtime.yml"
|
||||
- "scripts/verify-browser-runtime.mjs"
|
||||
- "scripts/lib/browser-runtime-*.mjs"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "scripts/fixtures/browser-runtime-site.mjs"
|
||||
- "src/browser/execution/**"
|
||||
- "src/browser/CdpConnection.ts"
|
||||
- "src/browser/CdpEndpoint.ts"
|
||||
- "src/browser/CdpTarget*.ts"
|
||||
- "src/browser/CdpCaptureValues.ts"
|
||||
- "src/browser/CdpAuthorized*.ts"
|
||||
- "src/domain/webRuntime.ts"
|
||||
- "src/domain/webExecution.ts"
|
||||
- "src/domain/webEventListeners.ts"
|
||||
- "src/domain/browserObservationError*.ts"
|
||||
- "src/application/WebRuntime*.ts"
|
||||
- "src/composition/webRuntime.ts"
|
||||
- "src/contracts/webRuntimeToolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/server/registerWebRuntimeTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webRuntimeCommands.ts"
|
||||
- "src/cli/commandCancellation.ts"
|
||||
- "src/cli.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-runtime-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:browser:runtime
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Real website source-map verification
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-source-map.yml"
|
||||
- "scripts/verify-browser-source-maps.mjs"
|
||||
- "scripts/lib/browser-source-map-e2e.mjs"
|
||||
- "scripts/fixtures/browser-source-map-site.mjs"
|
||||
- "src/javascript/sourceMaps/**"
|
||||
- "src/browser/assets/**"
|
||||
- "src/domain/webSource*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/application/WebSourceLocation*.ts"
|
||||
- "src/application/ValidateSelectedWebScript.ts"
|
||||
- "src/composition/webSourceLocations.ts"
|
||||
- "src/server/registerWebSourceLocationTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/process/**"
|
||||
- "src/cli/webSourceLocationCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-source-map-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-source-map:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install the pinned compiler into an isolated fixture directory
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
|
||||
- run: npm run verify:browser:source-maps
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
REA_WEB_SOURCE_MAP_COMPILER: ${{ runner.temp }}/rea-web-source-map-compiler/node_modules/esbuild/lib/main.js
|
||||
+251
-65
@@ -3,102 +3,275 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [closed]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_branch:
|
||||
description: Frozen release branch (release/VERSION)
|
||||
required: true
|
||||
type: string
|
||||
phase:
|
||||
description: Prepare the release PR, or publish its reviewed merge
|
||||
required: true
|
||||
default: prepare
|
||||
type: choice
|
||||
options: [prepare, publish]
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
group: release-${{ inputs.release_branch || github.event.pull_request.merge_commit_sha || github.ref_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
name: Release Please
|
||||
release-proposal:
|
||||
name: Update release proposal
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: googleapis/release-please-action@v4
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: main
|
||||
skip-github-release: true
|
||||
|
||||
merged-release:
|
||||
name: Release reviewed main merge
|
||||
if: >-
|
||||
github.event_name == 'pull_request' && github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
startsWith(github.event.pull_request.head.ref, 'release-please--branches--main--')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
sha: ${{ steps.release.outputs.sha }}
|
||||
steps:
|
||||
- name: Check out reviewed release merge
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Validate reviewed merge before release creation
|
||||
env:
|
||||
SOURCE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
run: |
|
||||
if [[ "${SOURCE_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The reviewed merge differs from the workflow source; refusing mismatched publication provenance." >&2
|
||||
exit 1
|
||||
fi
|
||||
node scripts/verify-release-checkpoint.mjs \
|
||||
--phase publish --release-branch main --source-sha "$SOURCE_SHA"
|
||||
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
# A dedicated credential lets release-PR updates trigger CI. GitHub
|
||||
# requires manual approval for pull_request events from GITHUB_TOKEN.
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: main
|
||||
skip-github-release: false
|
||||
skip-github-pull-request: true
|
||||
|
||||
- name: Bind publication to the reviewed merge
|
||||
env:
|
||||
RELEASE_CREATED: ${{ steps.release.outputs.release_created }}
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
SOURCE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
run: |
|
||||
if [[ "${RELEASE_CREATED}" != true ]]; then
|
||||
echo "Release Please did not create a release for the reviewed merge. Check its pending label and release metadata; for a partial publication, re-run failed jobs in the original run." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_SHA}" != "${SOURCE_SHA}" ]]; then
|
||||
echo "The tagged release differs from the reviewed merge; refusing mismatched publication provenance. The GitHub release already exists; do not create another release as a retry." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
release-please:
|
||||
name: Release Please
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
sha: ${{ steps.release.outputs.sha }}
|
||||
steps:
|
||||
- name: Validate release selection
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
RELEASE_PHASE: ${{ inputs.phase }}
|
||||
run: |
|
||||
if [[ "${RELEASE_PHASE}" != prepare && "${RELEASE_PHASE}" != publish ]]; then
|
||||
echo "phase must be prepare or publish." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_PHASE}" == prepare && "${GITHUB_REF}" != "refs/heads/main" ]]; then
|
||||
echo "Run prepare from main; release_branch selects the frozen source." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_PHASE}" == publish && "${GITHUB_REF}" != "refs/heads/${RELEASE_BRANCH}" ]]; then
|
||||
echo "Run publish from the selected frozen release branch so npm provenance records its source." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${RELEASE_BRANCH}" =~ ^release/[^/]+$ ]]; then
|
||||
echo "release_branch must select a release/ branch; main is not a release checkpoint." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! git check-ref-format "refs/heads/${RELEASE_BRANCH}"; then
|
||||
echo "Invalid release_branch: ${RELEASE_BRANCH}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Resolve release branch tip
|
||||
id: branch
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
run: |
|
||||
sha="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/${RELEASE_BRANCH}" --jq '.object | select(.type == "commit") | .sha')"
|
||||
if [[ ! "${sha}" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "The release branch tip is not a commit." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Require the publish dispatch to match the branch tip
|
||||
if: inputs.phase == 'publish'
|
||||
shell: bash
|
||||
env:
|
||||
BRANCH_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: |
|
||||
if [[ "${BRANCH_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The release branch tip differs from the dispatched checkpoint; refusing to create a release for a moved source." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out release controller and Git history
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js for checkpoint validation
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install checkpoint validation dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Validate checkpoint version and ancestry before release creation
|
||||
env:
|
||||
RELEASE_PHASE: ${{ inputs.phase }}
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: >-
|
||||
node scripts/verify-release-checkpoint.mjs
|
||||
--phase "$RELEASE_PHASE" --release-branch "$RELEASE_BRANCH"
|
||||
--source-sha "$SOURCE_SHA"
|
||||
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
# A dedicated credential starts PR CI automatically; the built-in
|
||||
# token creates approval-required PR runs for maintainers to approve.
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: ${{ inputs.release_branch }}
|
||||
skip-github-release: ${{ inputs.phase == 'prepare' }}
|
||||
skip-github-pull-request: ${{ inputs.phase == 'publish' }}
|
||||
|
||||
- name: Require a merged release PR for publication
|
||||
if: inputs.phase == 'publish' && steps.release.outputs.release_created != 'true'
|
||||
run: |
|
||||
echo "No new release was created. Merge the reviewed release PR into its frozen branch before publishing. For a partial publication, re-run failed jobs in the original run." >&2
|
||||
exit 1
|
||||
|
||||
- name: Bind publication to the dispatched checkpoint
|
||||
if: inputs.phase == 'publish' && steps.release.outputs.release_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
if [[ "${RELEASE_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The tagged release SHA differs from the dispatched checkpoint; refusing to publish mismatched source provenance. The GitHub release already exists; do not dispatch publish again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out release pull request
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js for generated documentation
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Validate prepared release version and migration notes
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
env:
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: >-
|
||||
node scripts/verify-release-checkpoint.mjs
|
||||
--phase prepare --stage candidate --release-branch "$RELEASE_BRANCH"
|
||||
--source-sha "$SOURCE_SHA"
|
||||
|
||||
- name: Regenerate release documentation
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm run docs:generate
|
||||
|
||||
- name: Commit canonical release catalog
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
# Use the same identity for the follow-up commit so it also triggers CI.
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
RELEASE_PR_JSON: ${{ steps.release.outputs.pr }}
|
||||
run: |
|
||||
release_pr_branch="$(node --input-type=module -e '
|
||||
const value = JSON.parse(process.env.RELEASE_PR_JSON ?? "");
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
value === null ||
|
||||
typeof value.headBranchName !== "string" ||
|
||||
value.headBranchName.length === 0
|
||||
) {
|
||||
process.exit(1);
|
||||
}
|
||||
process.stdout.write(value.headBranchName);
|
||||
')"
|
||||
changed_paths="$(git diff --name-only)"
|
||||
if [[ -z "${changed_paths}" ]]; then
|
||||
echo "Release documentation is already canonical."
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${changed_paths}" != "docs/product-catalog.json" ]]; then
|
||||
echo "docs:generate changed unexpected tracked paths:" >&2
|
||||
printf '%s\n' "${changed_paths}" >&2
|
||||
exit 1
|
||||
fi
|
||||
git diff --check
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -- docs/product-catalog.json
|
||||
git commit -m "chore: normalize release product catalog"
|
||||
gh auth setup-git
|
||||
git push origin "HEAD:${release_pr_branch}"
|
||||
- name: Validate generated release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm run docs:check && git diff --exit-code
|
||||
|
||||
publish:
|
||||
name: Publish to npm
|
||||
needs: release-please
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
needs: [merged-release, release-please]
|
||||
if: >-
|
||||
always() && !cancelled() &&
|
||||
((needs.merged-release.result == 'success' && needs.merged-release.outputs.release_created == 'true') ||
|
||||
(needs.release-please.result == 'success' && inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -108,6 +281,12 @@ jobs:
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Prepare Windows native build toolchain
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
|
||||
- name: Build and verify bundled Windows native controls
|
||||
run: npm run build:windows-native && npm run verify:windows-native:artifact
|
||||
|
||||
- name: Verify packaged release before publishing
|
||||
run: npm run verify:package
|
||||
|
||||
@@ -119,7 +298,8 @@ jobs:
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
echo "rea-agents@${version} is already published; verifying the existing package."
|
||||
else
|
||||
npm publish --access public
|
||||
tag="$(node scripts/release-npm-tag.mjs "${version}")"
|
||||
npm publish --access public --tag "${tag}"
|
||||
fi
|
||||
|
||||
- name: Verify published CLI from npm
|
||||
@@ -129,29 +309,35 @@ jobs:
|
||||
verification_root="$(mktemp -d)"
|
||||
trap 'rm -rf -- "${verification_root}"' EXIT
|
||||
cd "${verification_root}"
|
||||
for attempt in $(seq 1 20); do
|
||||
published_version="$(npm view "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
max_attempts=120
|
||||
sleep_seconds=15
|
||||
total_window_minutes=$((max_attempts * sleep_seconds / 60))
|
||||
for attempt in $(seq 1 "${max_attempts}"); do
|
||||
published_version="$(npm view --prefer-online "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
if npm exec --yes --package="rea-agents@${version}" -- rea --help \
|
||||
&& npm exec --yes --package="rea-agents@${version}" -- rea --llms \
|
||||
&& node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "Published rea-agents@${version} is visible, but CLI verification failed on attempt ${attempt}" >&2
|
||||
echo "rea-agents@${version} is visible on npm after attempt ${attempt}/${max_attempts}; verifying the published CLI."
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --help
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --llms
|
||||
node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"
|
||||
exit 0
|
||||
else
|
||||
echo "rea-agents@${version} is not visible on npm yet (attempt ${attempt}/20)" >&2
|
||||
echo "rea-agents@${version} is not visible on npm yet (attempt ${attempt}/${max_attempts}; up to ${total_window_minutes} minutes)" >&2
|
||||
fi
|
||||
if [ "${attempt}" -lt 20 ]; then
|
||||
sleep 15
|
||||
if [ "${attempt}" -lt "${max_attempts}" ]; then
|
||||
sleep "${sleep_seconds}"
|
||||
fi
|
||||
done
|
||||
echo "Published rea-agents@${version} failed registry and CLI verification" >&2
|
||||
echo "rea-agents@${version} did not become visible on npm within ${total_window_minutes} minutes (${max_attempts} attempts × ${sleep_seconds}s)" >&2
|
||||
echo "This indicates npm registry propagation delay, not a CLI verification failure." >&2
|
||||
exit 1
|
||||
|
||||
publish-mcp:
|
||||
name: Publish to MCP Registry
|
||||
needs: [release-please, publish]
|
||||
if: needs.release-please.outputs.release_created == 'true' && needs.publish.result == 'success'
|
||||
needs: [merged-release, release-please, publish]
|
||||
if: >-
|
||||
always() && !cancelled() && needs.publish.result == 'success' &&
|
||||
((needs.merged-release.result == 'success' && needs.merged-release.outputs.release_created == 'true') ||
|
||||
(needs.release-please.result == 'success' && inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -159,7 +345,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.tag_name }}
|
||||
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Verify package and registry metadata versions
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Website checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, "release/*"]
|
||||
paths:
|
||||
- "website/**"
|
||||
- "scripts/verify-website.py"
|
||||
- "scripts/prepare-website.py"
|
||||
- ".github/workflows/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-check-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Website checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
@@ -0,0 +1,169 @@
|
||||
name: Publish REA website
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target:
|
||||
description: Publish both hosts, or Pages while configuring Cloudflare
|
||||
type: choice
|
||||
required: true
|
||||
default: both
|
||||
options:
|
||||
- both
|
||||
- pages
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
pages: read
|
||||
steps:
|
||||
- name: Validate the publication target
|
||||
env:
|
||||
PUBLISH_TARGET: ${{ inputs.target }}
|
||||
run: |
|
||||
case "$PUBLISH_TARGET" in
|
||||
both|pages) ;;
|
||||
*) echo "::error::Unknown publication target: $PUBLISH_TARGET"; exit 1 ;;
|
||||
esac
|
||||
- name: Require Cloudflare publishing credentials
|
||||
if: inputs.target == 'both'
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
run: |
|
||||
if [[ -z "$CLOUDFLARE_API_TOKEN" || -z "$CLOUDFLARE_ACCOUNT_ID" ]]; then
|
||||
echo "::error::Set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in repository Actions secrets before publishing."
|
||||
exit 1
|
||||
fi
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
- name: Record the published commit
|
||||
run: |
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
Path("website/public/deployment.json").write_text(
|
||||
json.dumps({"commit": os.environ["GITHUB_SHA"]}) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
PY
|
||||
- name: Configure Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload the website
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: website/public
|
||||
|
||||
deploy:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Check out publishing configuration
|
||||
if: inputs.target == 'both'
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js for Wrangler
|
||||
if: inputs.target == 'both'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Download the verified website
|
||||
if: inputs.target == 'both'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: github-pages
|
||||
path: ${{ runner.temp }}/website-archive
|
||||
- name: Extract the shared publishing artifact
|
||||
if: inputs.target == 'both'
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/rea-website-public"
|
||||
tar -xf "$RUNNER_TEMP/website-archive/artifact.tar" -C "$RUNNER_TEMP/rea-website-public"
|
||||
- name: Publish the website to Cloudflare
|
||||
if: inputs.target == 'both'
|
||||
uses: cloudflare/wrangler-action@v4
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
wranglerVersion: "4.149.0"
|
||||
packageManager: npm
|
||||
command: deploy --cwd website --env production --assets "${{ runner.temp }}/rea-website-public"
|
||||
- name: Publish to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
- name: Verify the selected published revisions
|
||||
env:
|
||||
PAGES_URL: ${{ steps.deployment.outputs.page_url }}
|
||||
PUBLISH_TARGET: ${{ inputs.target }}
|
||||
run: |
|
||||
python3 -u - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from urllib.error import URLError
|
||||
from urllib.parse import urljoin
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
expected = os.environ["GITHUB_SHA"]
|
||||
pages_url = os.environ["PAGES_URL"]
|
||||
if not pages_url:
|
||||
sys.exit("GitHub Pages returned no published URL.")
|
||||
target = os.environ["PUBLISH_TARGET"]
|
||||
if target not in ("both", "pages"):
|
||||
sys.exit("Unknown publication target: " + target)
|
||||
hosts = {"GitHub Pages": pages_url}
|
||||
if target == "both":
|
||||
hosts["Cloudflare"] = "https://rea.tools/"
|
||||
pending = dict(hosts)
|
||||
for attempt in range(24):
|
||||
for name, base in list(pending.items()):
|
||||
url = urljoin(base, "deployment.json") + "?commit=" + expected
|
||||
try:
|
||||
request = Request(url, headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"User-Agent": "REA-Website-Verifier/1.0 (+https://github.com/morluto/rea)",
|
||||
})
|
||||
with urlopen(request, timeout=10) as response:
|
||||
actual = json.load(response).get("commit")
|
||||
if actual == expected:
|
||||
print(f"{name} serves {expected}: {base}")
|
||||
del pending[name]
|
||||
else:
|
||||
print(f"Waiting for {name}: observed revision {actual}")
|
||||
except (URLError, TimeoutError, ValueError) as error:
|
||||
print(f"Waiting for {name}: {error}")
|
||||
if not pending:
|
||||
break
|
||||
if attempt < 23:
|
||||
time.sleep(5)
|
||||
if pending:
|
||||
sys.exit("Could not verify published revision on: " + ", ".join(pending))
|
||||
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
|
||||
links = " and ".join(f"[{name}]({url})" for name, url in hosts.items())
|
||||
summary.write(f"Published commit `{expected}` to {links}.\n")
|
||||
PY
|
||||
+20
-1
@@ -6,7 +6,6 @@ __pycache__/
|
||||
# Distribution
|
||||
dist/
|
||||
build/
|
||||
docs/api/
|
||||
node_modules
|
||||
*.egg-info/
|
||||
*.egg
|
||||
@@ -38,6 +37,8 @@ coverage/
|
||||
test-results.xml
|
||||
.turbo/
|
||||
.cache/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
@@ -45,3 +46,21 @@ Thumbs.db
|
||||
|
||||
# Local reference sources and analysis artifacts
|
||||
/_reference/
|
||||
|
||||
# Generated website download
|
||||
/website/public/examples/notes-example.zip
|
||||
/website/public/deployment.json
|
||||
|
||||
# Local Cloudflare development state
|
||||
/website/.wrangler/
|
||||
/website/.dev.vars*
|
||||
/website/.env*
|
||||
!/website/.dev.vars.example
|
||||
!/website/.env*.example
|
||||
|
||||
# Derived catalogs, portable conformance projections, and packaged skill bundles
|
||||
# (regenerated by `npm run docs:generate`; never commit)
|
||||
/skills/
|
||||
/docs/public/product-catalog.json
|
||||
/docs/verification/managed-conformance-manifest.json
|
||||
/docs/verification/managed-conformance-ledger.json
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[submodule "third_party/jadx-headless-mcp"]
|
||||
path = third_party/jadx-headless-mcp
|
||||
url = https://github.com/1013503897/jadx-headless-mcp.git
|
||||
[submodule "third_party/binwalk"]
|
||||
path = third_party/binwalk
|
||||
url = https://github.com/ReFirmLabs/binwalk.git
|
||||
[submodule "third_party/unblob"]
|
||||
path = third_party/unblob
|
||||
url = https://github.com/onekey-sec/unblob.git
|
||||
[submodule "third_party/ghidra-nativeaot"]
|
||||
path = third_party/ghidra-nativeaot
|
||||
url = https://github.com/Washi1337/ghidra-nativeaot.git
|
||||
[submodule "third_party/wakaru"]
|
||||
path = third_party/wakaru
|
||||
url = https://github.com/pionxzh/wakaru.git
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_comment": "Test files are intentionally NOT excluded: the gate used to report zero clones because it skipped **/*.test.ts, hiding all test-tree duplication. Current actual is ~2.6% lines across src+ and tests/, down from 3.4% when tests were included. Lower this as duplication is removed.",
|
||||
"threshold": 2.7,
|
||||
"_comment": "Test files are intentionally NOT excluded: the gate used to report zero clones because it skipped **/*.test.ts, hiding all test-tree duplication. Current actual is ~2.4% lines across src+ and tests/, down from 3.4% when tests were included. Lower this as duplication is removed.",
|
||||
"threshold": 2.5,
|
||||
"reporters": ["consoleFull"],
|
||||
"ignore": [
|
||||
"node_modules/**",
|
||||
@@ -9,6 +9,7 @@
|
||||
".codex/**",
|
||||
".devin/**",
|
||||
"tests/fixtures/**",
|
||||
"third_party/**",
|
||||
"tests/conformance/**/*.c",
|
||||
"tests/conformance/**/*.m",
|
||||
"tests/conformance/**/*.swift",
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@
|
||||
"node_modules/**",
|
||||
"CHANGELOG.md",
|
||||
"package-lock.json",
|
||||
"_reference/**"
|
||||
"_reference/**",
|
||||
"third_party/**"
|
||||
]
|
||||
}
|
||||
|
||||
+30
-42
@@ -6,11 +6,19 @@
|
||||
"max-depth": ["warn", 4],
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{ "max": 500, "skipBlankLines": true, "skipComments": true }
|
||||
{
|
||||
"max": 500,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-lines-per-function": [
|
||||
"warn",
|
||||
{ "max": 100, "skipBlankLines": true, "skipComments": true }
|
||||
{
|
||||
"max": 100,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-params": ["warn", 4],
|
||||
"no-var": "warn",
|
||||
@@ -19,22 +27,12 @@
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"src/{contracts,domain,server}/**/*.ts",
|
||||
"src/application/**/*.ts"
|
||||
"scripts/electron-active-hook*.cjs",
|
||||
"tests/conformance/readiness/electron/*.js",
|
||||
"website/public/examples/notes-electron/*.js"
|
||||
],
|
||||
"excludeFiles": ["src/application/runtime.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["**/ghidra/**", "**/hopper/**"],
|
||||
"message": "Deep-provider imports belong behind adapters or in the application composition root."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"no-var-requires": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -42,33 +40,11 @@
|
||||
"rules": {
|
||||
"max-lines": "off",
|
||||
"max-lines-per-function": [
|
||||
"error",
|
||||
{ "max": 150, "skipBlankLines": true, "skipComments": true }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/domain/**/*.{fixture,test}.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": [
|
||||
"**/application/**",
|
||||
"**/server/**",
|
||||
"**/process/**",
|
||||
"**/browser/**",
|
||||
"**/ghidra/**",
|
||||
"**/hopper/**",
|
||||
"**/replay/**",
|
||||
"**/cli.*",
|
||||
"**/main.*"
|
||||
],
|
||||
"message": "Domain tests may import only their owning domain and inward dependencies."
|
||||
}
|
||||
]
|
||||
"max": 150,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -120,5 +96,17 @@
|
||||
"node": true,
|
||||
"es2023": true
|
||||
},
|
||||
"ignorePatterns": ["dist", "node_modules", "docs/api", ".codex", ".devin"]
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"build",
|
||||
"node_modules",
|
||||
"docs/api",
|
||||
"coverage",
|
||||
".turbo",
|
||||
".cache",
|
||||
".codex",
|
||||
".devin",
|
||||
".factory",
|
||||
"third_party"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "4.0.0"
|
||||
".": "6.1.0"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Product Direction
|
||||
|
||||
REA exposes reverse-engineering tools through a CLI and MCP server. Hopper and the bring-your-own Ghidra adapter are operation-capable deep binary-analysis providers. Ghidra is supported on Linux x64 and macOS x64/arm64 with matching native decompiler tools, and has an experimental Windows x64 P0 boundary for approved native x86-64 PE applications; it supplies admitted read-only inventory and function-analysis operations but no GUI or mutation authority. Keep provider-specific code out of the domain and application layers.
|
||||
REA exposes reverse-engineering tools through a CLI and MCP server. Hopper, the bring-your-own Ghidra adapter, and the bring-your-own IDA MCP adapter are operation-capable deep binary-analysis providers. Ghidra is supported on Linux x64/arm64 and macOS x64/arm64 with matching native decompiler tools, and has an experimental Windows x64 P0 boundary for approved native x86 and x86-64 PE applications; on Linux and macOS it supplies inventory, function analysis, and atomic function annotation edits in an ephemeral database, without modifying executable bytes or controlling a GUI. Windows P0 has no mutation authority. IDA adapts upstream legacy attached GUI and modern headless database-supervisor profiles for live read-only analysis; initial real verification covers Windows, and an attached GUI database is never saved or closed. Keep provider-specific code out of the domain and application layers.
|
||||
|
||||
Prioritize:
|
||||
|
||||
@@ -20,22 +20,23 @@ REA is a local-only tool. Preserve caller-selected inputs, captured output, URLs
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward from pure domain logic through contracts, providers, application workflows, and CLI/MCP adapters. See [docs/architecture.mermaid](docs/architecture.mermaid) for the component map.
|
||||
|
||||
- `src/domain/` owns pure provider-neutral semantics; `src/contracts/` owns caller-visible schemas and the canonical tool inventory.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/browser/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/application/` composes shared CLI/MCP workflows; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` contains real-toolchain checks.
|
||||
- `docs/product-catalog.json` is generated. Update its source contracts and regenerate it; do not edit it directly.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/ida/`, `src/browser/`, `src/inspector/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/application/` composes shared CLI/MCP workflows, including Evidence provenance, unknowns, and eligible snapshot bindings; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. Reuse its supervision and identity primitives before adding provider-local lifecycle code; a PID and executable pathname alone do not establish ownership after exit or reuse. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` and capability directories under `scripts/verify/` contain real-toolchain checks.
|
||||
- `docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored build outputs. Update source contracts and authored instructions in `skill-src/`, then run `npm run docs:generate`; never commit derived catalog digests or portable conformance projections.
|
||||
- `.cache/mcp-tool-catalog.json` is generated test metadata. Generate it with `npm run mcp-catalog:generate`; source checking and runtime builds do not consume it.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
- `npm ci`: install the locked dependencies.
|
||||
- `npm run build:cached`: build the CLI and MCP server.
|
||||
- `npm run build:cached`: compile the CLI/MCP runtime and bundle its authored skill. Test catalogs and documentation/conformance artifacts have separate tasks.
|
||||
- `npm run test:local`: run changed source tests without building; pass exact source test paths to run them regardless of Git status.
|
||||
- `npm run test:focused -- PATH...`: run exact test files; build first for boundary, acceptance, or process-global tests.
|
||||
- `npm run test:focused -- PATH...`: run exact test files, preparing runtime and generated artifacts required by the selected tests.
|
||||
- `npm run check:changed`: run cached static checks and source tests affected since the branch merge base (default `origin/main`).
|
||||
- `npm run check:fast`: run cached typecheck and lint checks.
|
||||
- `npm run check:pr`: opt into the complete local deterministic gate and generated-document checks for broad changes; CI owns full coverage. Routine iterations need focused tests and relevant checks, not the whole gate each time.
|
||||
- `npm run docs:check`: check committed generated documents; `npm run docs:api:cached` separately renders API HTML when needed.
|
||||
- `npm run docs:check`: build and validate generated documents for the current checkout; `npm run docs:generate` regenerates them. CI retains ignored outputs as artifacts and does not push snapshot commits to feature branches.
|
||||
- For provider-dependent changes, see [docs/testing.md](docs/testing.md) and run the matching real-provider verification.
|
||||
- Keep each verification lane's prerequisites limited to the claim it checks. Use host-native fixtures for host/provider acceptance; put optional cross-target formats and their external toolchains in a separate lane. Preflight required commands and report the missing dependency and lane clearly.
|
||||
|
||||
@@ -49,13 +50,19 @@ Configuration is parsed and validated by `src/config.ts` and `src/config/`. Keep
|
||||
|
||||
Use ESM TypeScript, two-space indentation, and the committed Oxfmt configuration. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers.
|
||||
|
||||
Assess brittleness during implementation, debugging, review, and maintenance. Simplify duplicated rules, scattered defaults, caller-specific exceptions, and unclear invariants in touched code. Prefer standard format-aware parsers and existing shared helpers; normalize once at the owning boundary while preserving source evidence. Keep refactors focused on the task, and introduce abstractions only when they remove observed complexity. Verify preserved behavior and corrected edge cases through affected callers.
|
||||
|
||||
## Boundary Contracts
|
||||
|
||||
Treat a boundary as a contract between the producer's actual representation and the consumer's required meaning. When implementing or auditing a boundary, trace the value through parsing, normalization, authorization, serialization, and the CLI/MCP result. Establish affected callers from their code paths; similar tool names or workflows do not prove that they share a schema or failure mode.
|
||||
|
||||
Keep portable evidence and scenario validation distinct from host-native execution checks. Absolute filesystem paths, file URLs, and HTTP paths have different semantics; do not substitute one platform's syntax for the domain concept. Interpret provider metadata according to its documented or observed producer behavior. When a transformation loses information, preserve the reported value and an explicit unknown rather than guessing a canonical identity.
|
||||
When changing boundary behavior, inspect adjacent input representations, failure paths, and affected callers, and correct the underlying assumption across those cases.
|
||||
|
||||
Preserve meaningful failure reasons through application and adapter layers. Malformed input is distinct from an unsupported target, unavailable provider, or host operating-system permission denial. Diagnostics should identify the failed constraint and the target or lifecycle request it applies to. Recovery advice must address that reason and point to an available workflow; generic catches must not erase actionable validation details.
|
||||
Keep portable evidence and scenario validation distinct from host-native execution checks. Absolute filesystem paths, file URLs, and HTTP paths have different semantics; do not substitute one platform's syntax for the domain concept. Interpret provider metadata according to its documented or observed producer behavior. Keep values used for identity, provenance, matching, and path resolution separate from display formatting. When normalization loses information, preserve the source value and an explicit unknown; display placeholders must not feed back into lookup or selection.
|
||||
|
||||
Preserve meaningful failure reasons through application and adapter layers. Malformed input is distinct from an unsupported target, unavailable provider, or host operating-system permission denial. Diagnostics should identify the failed constraint and the target or lifecycle request it applies to. Recovery advice must address that reason and point to an available workflow; generic catches must not erase actionable validation details. Preserve collected observations on execution failure independently of whether cleanup succeeds.
|
||||
|
||||
Preserve partial native facts. A shared schema must not require one provider's full metadata before another provider can report the facts it observes. Keep observed classifications and endpoints alongside explicit unknowns for unsupported flags; never invent missing flags or discard known facts to satisfy an all-or-nothing availability shape. Before claiming a native API lacks a capability, inspect the installed API and probe representative producer objects. Exercise the resulting partial representation through CLI, MCP, and composed results such as function dossiers.
|
||||
|
||||
Leave meaningful target, action, capture, and output choices to the agent. A selected operation already expresses intent; do not require approval booleans or repeated permission declarations. Trace each setting to its consumer: remove ignored options and single-value confirmations, derive built-in lifecycle behavior, and supply defaults for omitted optional metadata. Report actual effects and limitations where they help interpret results rather than asking callers to restate them.
|
||||
|
||||
@@ -79,7 +86,7 @@ Let agents compose experiments with ordinary commands, scripts, and local fixtur
|
||||
- Use **observe/capture** only when runtime activity is required, and declare authority and lifecycle effects in the contract.
|
||||
- Extend an existing tool when intent and result contract are unchanged. Add a tool for a distinct analyst outcome or materially different authority.
|
||||
- Keep caller-facing names and results provider-neutral. Put engine-specific behavior in provider adapters and report each provider's exact coverage.
|
||||
- Keep results complete by default. Add a limit only when it follows from a real format, protocol, authority, or resource-safety constraint; explain truncation and unsupported facets. Keep observed, derived, inferred, and unknown results distinct.
|
||||
- Keep results complete by default. Add a limit only when it follows from a real format, protocol, authority, or resource-safety constraint; explain truncation and unsupported facets. Account for representation expansion and products of independently bounded dimensions before allocating or retaining output. Derive facet completeness from what was examined and exhausted, not an empty failure list. Keep observed, derived, inferred, and unknown results distinct.
|
||||
- Include artifact identity, source locations, Evidence references, actionable errors, and relevant limitations when they affect conclusions. Return the evidence needed for the next analysis inline rather than requiring a resource or identifier lookup.
|
||||
- Implement shared application workflows behind CLI and MCP adapters. Update canonical contracts and generated catalog together.
|
||||
|
||||
@@ -87,10 +94,31 @@ See [docs/tool-design.md](docs/tool-design.md) for the design checklist. When us
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
Prefer full end-to-end workflows through the public CLI or MCP with real providers and no mocked dependencies, then integration across production boundaries, then goldens captured from real producers. Keep a focused module test only for a distinct failure or semantic case that stronger workflows cannot reliably reproduce. Delete redundant getter, serialization, enum, count, and snapshot assertions when a stronger consumer workflow already proves the claim. Test paths, compiled imports, and suites named E2E do not establish end-to-end coverage. See [docs/testing.md](docs/testing.md) for pruning and classification rules.
|
||||
|
||||
Treat advertised examples as executable contracts: derive them from representative producer data, exercise them through the advertised CLI or MCP workflow, and assert the behavior they claim to demonstrate. Share production projections where practical and declare omitted coverage explicitly.
|
||||
|
||||
Name tests `*.test.ts`. Use Vitest and production seams (`tests/fixtures/`) rather than module mocks. Domain tests assert pure behavior; adapter tests use fake launcher/socket seams; MCP tests connect with the client SDK version pinned in `package.json`. Preserve the canonical tool inventory defined by `TOOL_CONTRACTS` and verified through `CATALOG_IDENTITY` and generated product metadata. Cover malformed input, cancellation, lifecycle cleanup, and actual format, protocol, host-permission, and target-identity boundaries. Do not add tests that merely freeze arbitrary caps or prescribed call sequences. Real Hopper, Ghidra, browser, managed conformance, and any real managed-tool claims cannot be replaced by mocks; use the corresponding `verify:*` command.
|
||||
|
||||
Keep tool catalogs complete and self-describing; prefer capability- and session-scoped availability over schema truncation. Serialized bytes alone do not measure agent usability or model context cost.
|
||||
|
||||
## Cursor Cloud specific instructions
|
||||
|
||||
Development requires Node.js 24.18.0 and npm 11.16.0 (`.nvmrc` and `packageManager`). The Cloud Agent image places an older `node` on `PATH` ahead of a normal install. Environment setup installs the pinned toolchain under `/usr/local` and prepends `/usr/local/bin` for login shells. Confirm `node -v` is `v24.18.0` before installing dependencies.
|
||||
|
||||
`npm ci` installs locked dependencies. `npm run build:cached` produces the CLI and MCP server. `npm run check:fast` is the pre-push typecheck and lint. Hopper, Ghidra, and IDA are optional bring-your-own providers. JavaScript analysis and the deterministic Vitest suites do not need them. `rea doctor` reports those engines as missing until they are configured.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Use Conventional Commit subjects because Release Please derives versions and changelogs from them. Examples: `feat: add historical source import`, `fix(process): stop timers after exit`, and `docs: update architecture`. Use `!` or a `BREAKING CHANGE:` footer for breaking changes. Pull request titles must follow the same format because squash merges use the title as the release commit. Pull requests should describe contract or behavior changes, list verification commands, link issues, and include sanitized MCP examples when schemas change. State whether real Hopper/Ghidra verification was performed. Never commit binaries, Hopper or Ghidra project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts (e.g. `.codex/`).
|
||||
Use Conventional Commit subjects because Release Please derives changelogs from them. Release Please uses `always-bump-minor`: every release increments minor, including breaking changes. Examples: `feat: add historical source import`, `fix(process): stop timers after exit`, and `docs: update architecture`. Use `!` or a `BREAKING CHANGE:` footer for breaking changes. Pull request titles must follow the same format because squash merges use the title as the release commit. Pull requests should describe contract or behavior changes, list verification commands, link issues, and include sanitized MCP examples when schemas change. State whether real Hopper/Ghidra verification was performed. Never commit binaries, Hopper or Ghidra project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts (e.g. `.codex/`).
|
||||
|
||||
<!-- BEGIN:turborepo-agent-rules -->
|
||||
|
||||
# This is NOT the Turborepo you know
|
||||
|
||||
Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.
|
||||
|
||||
Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
|
||||
|
||||
This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
|
||||
<!-- END:turborepo-agent-rules -->
|
||||
|
||||
+717
@@ -1,5 +1,722 @@
|
||||
# Changelog
|
||||
|
||||
## [6.1.0](https://github.com/morluto/rea/compare/rea-agents-6.0.0...rea-agents-6.1.0) (2026-10-09)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **hopper:** Hopper regex mode uses ECMAScript Unicode syntax instead of Python re. Matching runs in a cancellable worker with a five-second deadline; literal mode keeps Unicode casefold matching.
|
||||
* **hopper:** remove set_current_document. Use open_binary to switch targets; document arguments are scoped to the active target.
|
||||
|
||||
### Features
|
||||
|
||||
* **clients:** register Grok Build and Grok Bot during setup ([#1134](https://github.com/morluto/rea/issues/1134)) ([08561e0](https://github.com/morluto/rea/commit/08561e0eb7a4a01a256f3693a97061fbea7fb8fa))
|
||||
* **ghidra:** make the startup deadline configurable ([#974](https://github.com/morluto/rea/issues/974)) ([d84bc8f](https://github.com/morluto/rea/commit/d84bc8f11abd6cbf5612daf5c8f7e1805d23c16e))
|
||||
* **website:** add GitHub link with live star count ([2f59287](https://github.com/morluto/rea/commit/2f59287f1a1794a68102a45c61cc657659fa5de6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analysis:** bind overview document identity to the active target ([#1094](https://github.com/morluto/rea/issues/1094)) ([81b5104](https://github.com/morluto/rea/commit/81b51042767ad75310bad1a7a9faf954339841c0))
|
||||
* **analysis:** resolve procedure identities and preserve literal queries ([d830cb7](https://github.com/morluto/rea/commit/d830cb79b305e6d4d255f8080de69ca668cbe0af))
|
||||
* **android:** report actual provider readiness ([c715af8](https://github.com/morluto/rea/commit/c715af80587f808de816d3add6a6f989437ab66a))
|
||||
* **android:** report startup and selector failure causes ([6c13fc8](https://github.com/morluto/rea/commit/6c13fc8e0a318ebe7be6b038089b350d99f8e286))
|
||||
* **apple:** account for repeated archive projections ([e1df5e8](https://github.com/morluto/rea/commit/e1df5e85551125b27ef0debb7573de5507b149e1))
|
||||
* **apple:** budget Interface Builder decoding and projection ([dbcf8c3](https://github.com/morluto/rea/commit/dbcf8c33915d924fdbc881bf2cf9676587f3b96f))
|
||||
* **apple:** classify empty dyld settings by consumer semantics ([#1110](https://github.com/morluto/rea/issues/1110)) ([fe297e5](https://github.com/morluto/rea/commit/fe297e56e804130dd49cc0a95fcf2d115c183ae3))
|
||||
* **apple:** decode bundle plist bytes before executable lookup ([d575a73](https://github.com/morluto/rea/commit/d575a7340bb19b84733c79b14de9f4df7181c6b3))
|
||||
* **apple:** decode Interface Builder XML byte encodings ([#1095](https://github.com/morluto/rea/issues/1095)) ([f8e143a](https://github.com/morluto/rea/commit/f8e143a3bcd20ddcc0d76d90e73e238d456fe5a3))
|
||||
* **apple:** derive dispatch coverage from located decode facts ([488f448](https://github.com/morluto/rea/commit/488f448ae27fd62904967ec1e0ab072e834a9822))
|
||||
* **apple:** keep skipped catalog entries from reading as absence ([a079af2](https://github.com/morluto/rea/commit/a079af297bb454417dfd3d89b0037397554ad83e))
|
||||
* **apple:** report apps without asset catalogs as empty inventories ([e2f0378](https://github.com/morluto/rea/commit/e2f03786f496ed742572acd87f47270c958171ec))
|
||||
* **apple:** report apps without asset catalogs as empty inventories ([79e5a39](https://github.com/morluto/rea/commit/79e5a3923db896323c0491e31004214856529b74))
|
||||
* **apple:** report missing keyed archive hierarchy references ([#1104](https://github.com/morluto/rea/issues/1104)) ([3b2ec69](https://github.com/morluto/rea/commit/3b2ec69c54fc9cf65bf8441edbb96e9619c3a37e))
|
||||
* **apple:** validate dylib candidates and preserve search uncertainty ([#1100](https://github.com/morluto/rea/issues/1100)) ([7914e36](https://github.com/morluto/rea/commit/7914e36ab9adbbf591a6e1f3fafcb2e17f55d667))
|
||||
* **apple:** validate physical slices and dylib platform compatibility ([15f92f2](https://github.com/morluto/rea/commit/15f92f298891c8b16d7d13c78cb6ca66cb90cd85))
|
||||
* **application:** budget bridge candidate projection by bytes ([3d450a2](https://github.com/morluto/rea/commit/3d450a29fc91cef3e06ffce9a925ac70dcb24272))
|
||||
* **artifacts:** preserve keyed archive integer precision ([4034883](https://github.com/morluto/rea/commit/4034883e9860a97edee03260fab59f9229debfa6))
|
||||
* **artifacts:** report keyed-archive selection mistakes as invalid input ([5a55d5b](https://github.com/morluto/rea/commit/5a55d5b1c6aefb4a4e5df63ec43b7e2fc0a7d385))
|
||||
* **artifacts:** report keyed-archive selection mistakes as invalid input ([25fb6d3](https://github.com/morluto/rea/commit/25fb6d32a325d88bb0f4e05e16f5f82e4f91c5ca))
|
||||
* **artifacts:** retain failed DMG cleanup state and diagnostics ([#1103](https://github.com/morluto/rea/issues/1103)) ([4bb056b](https://github.com/morluto/rea/commit/4bb056bbb5b735e214ed8d725aa3de1c06fb0dc9))
|
||||
* **browser:** advertise zero-based units for runtime source coordinates ([#1085](https://github.com/morluto/rea/issues/1085)) ([6218d28](https://github.com/morluto/rea/commit/6218d289e241715d0868004caafe15b5c4758450))
|
||||
* **browser:** bind correlated replies to their selected session ([96d3caf](https://github.com/morluto/rea/commit/96d3cafb0725881b6cdab0e2185104957dc69f71))
|
||||
* **browser:** bound aggregate source-map decoding before expansion ([f983c71](https://github.com/morluto/rea/commit/f983c718c33789ee40aec43f84315f6b53e95bde))
|
||||
* **browser:** bound script metadata and preserve omissions ([faf209e](https://github.com/morluto/rea/commit/faf209e50d9faab03a185c1f044de3582552d11e))
|
||||
* **browser:** bound source-map fetches and retain predecessor requests ([eaf5734](https://github.com/morluto/rea/commit/eaf57340831a753faec58246290f9502d159b6b1))
|
||||
* **browser:** compose nested source-map column offsets ([#1109](https://github.com/morluto/rea/issues/1109)) ([8b08ec0](https://github.com/morluto/rea/commit/8b08ec0af936405fd20db25f941e2ae6318c5145))
|
||||
* **browser:** keep initiator callsites out of script coordinate guidance ([#1096](https://github.com/morluto/rea/issues/1096)) ([3c157b4](https://github.com/morluto/rea/commit/3c157b4ec6297e6737bd3c2ec3dc520c7403402f))
|
||||
* **browser:** normalize selected script manifest roots on Windows ([#1078](https://github.com/morluto/rea/issues/1078)) ([b49beb4](https://github.com/morluto/rea/commit/b49beb42d7a97f73d052a27fb99673b1bd0d3d39))
|
||||
* **browser:** preserve captures when response bodies are unavailable ([96af2fa](https://github.com/morluto/rea/commit/96af2fa7444a3777d51ff46202ef563146929dbd))
|
||||
* **browser:** preserve command failures and reject malformed replies ([6b92002](https://github.com/morluto/rea/commit/6b92002597d34555c32ebf24b9db6e4c17c0a08d))
|
||||
* **browser:** treat module-import initiator positions as script positions ([ae0630a](https://github.com/morluto/rea/commit/ae0630a619ee2c98a68e65ca44bf0b93424e2f8e))
|
||||
* **browser:** treat module-import initiator positions as script positions ([99d31ee](https://github.com/morluto/rea/commit/99d31ee24ad060babace9eca3325858418e0f55a))
|
||||
* **browser:** validate and bound source-map evidence ([0a0174b](https://github.com/morluto/rea/commit/0a0174b9ac8a73b61094e857a2950f4e5a06f544))
|
||||
* **build:** exclude the test-only generated catalog from packages ([#1116](https://github.com/morluto/rea/issues/1116)) ([d1c904b](https://github.com/morluto/rea/commit/d1c904bac7977d44b06bf2f823587baedcc88b9e))
|
||||
* **build:** make catalogs and packaged skills generated outputs ([#1086](https://github.com/morluto/rea/issues/1086)) ([e613405](https://github.com/morluto/rea/commit/e6134056cf519410160a196363f8a04f0729324e))
|
||||
* **capture:** retain Electron observations and HAR resource failures ([44fd183](https://github.com/morluto/rea/commit/44fd18305fa5a8893ebb5b952099fdf45dbd6e55))
|
||||
* **ci:** align diagnostic regressions and format merged changes ([67756da](https://github.com/morluto/rea/commit/67756dacc0d435a61d2d0c403412151769da028f))
|
||||
* **ci:** restore generated shard inputs and cleanup assertions ([5f93b4b](https://github.com/morluto/rea/commit/5f93b4b125e78f088a2349a178e60f8c0af3e1a2))
|
||||
* **cli:** accept exact native names through explicit selectors ([c313606](https://github.com/morluto/rea/commit/c3136064c7f8f72b2f6c6799ec41ea6424111bb7))
|
||||
* **cli:** drive mcp doctor help from a single option schema ([b7d803d](https://github.com/morluto/rea/commit/b7d803dd10c7ef8bb73f81f5e893168af8ea86e6))
|
||||
* **cli:** preserve blank JSON paths for input validation ([#1068](https://github.com/morluto/rea/issues/1068)) ([3dcb732](https://github.com/morluto/rea/commit/3dcb732da33f6ceef597506b14a4536f1c9aff96))
|
||||
* **cli:** preserve web cancellation exit status ([1d9b96a](https://github.com/morluto/rea/commit/1d9b96a9c497694592137cf95bea2af0a190abe7))
|
||||
* **cli:** support MCP doctor help ([a69b833](https://github.com/morluto/rea/commit/a69b8337ab38707aaad2ac2fedb652f05afeb71c))
|
||||
* **contracts:** preserve local inputs and comparison uncertainty ([70e8a8f](https://github.com/morluto/rea/commit/70e8a8f86060496452ccbc1a49b39822efef4113))
|
||||
* **docs:** validate translated links to canonical product facts ([34f7c4f](https://github.com/morluto/rea/commit/34f7c4fd661e07db66c560a4d54272b0163a52b6))
|
||||
* **domain:** order canonical paths by code point, not UTF-16 unit ([#1034](https://github.com/morluto/rea/issues/1034)) ([b5c2954](https://github.com/morluto/rea/commit/b5c2954ad67f0280732df043d5d9702f3d202819))
|
||||
* **domain:** preserve prototype-named JSON members through Evidence boundaries ([#1049](https://github.com/morluto/rea/issues/1049)) ([40e18b2](https://github.com/morluto/rea/commit/40e18b2e37f2be11f452606c2d875981b03ad03e))
|
||||
* **domain:** reject excessive JSON depth with actionable diagnostics ([#1029](https://github.com/morluto/rea/issues/1029)) ([bfbfc73](https://github.com/morluto/rea/commit/bfbfc7330aa7175018de04f1d099b8d4aeec3bd1))
|
||||
* **electron:** bound hook retention and report dropped coverage ([02d0702](https://github.com/morluto/rea/commit/02d07027617a5df014f92a351aa9418aade6f06f))
|
||||
* **evidence:** name the failed constraint when a bundle is rejected ([82a1a52](https://github.com/morluto/rea/commit/82a1a525d7aa725d939926ba953434fbf5db0ab0))
|
||||
* **evidence:** name the failed constraint when a bundle is rejected ([cf96ceb](https://github.com/morluto/rea/commit/cf96ceb2119f52f61a621d2a790b7c8804c65595))
|
||||
* **evidence:** report missing evidence files instead of permission advice ([e8b92a4](https://github.com/morluto/rea/commit/e8b92a40c003a5e6ce3cefe4ad18392d39759653))
|
||||
* **evidence:** report missing evidence files instead of permission advice ([89252f1](https://github.com/morluto/rea/commit/89252f1f35de91494bd49460b1645d8c09cc6d33))
|
||||
* **evidence:** require a complete record before the single-record hint ([38bbb65](https://github.com/morluto/rea/commit/38bbb656da2659088e30a1ca3e4434b2f65bab32))
|
||||
* **firmware:** reject malformed UTF-8 producer reports ([4a45c14](https://github.com/morluto/rea/commit/4a45c14742e6868f687cead2ddf736c3f7c49acd))
|
||||
* **ghidra:** admit Linux arm64 with matching native tools ([3e598b2](https://github.com/morluto/rea/commit/3e598b2d38a728cd9fd4a359741500a021ba9d3a))
|
||||
* **ghidra:** harden location resolution and provider lifecycle ([1f5be75](https://github.com/morluto/rea/commit/1f5be750bced759339183945488cea352b18f28b))
|
||||
* **ghidra:** make qualified function renames idempotent ([4458128](https://github.com/morluto/rea/commit/4458128a845c1448140b44d6e62d6ccfe611d37c))
|
||||
* **ghidra:** parse long procedure selectors without recursion ([480b16b](https://github.com/morluto/rea/commit/480b16b9c151e1d4bfbca8b49a0668d74ceaa400))
|
||||
* **ghidra:** preserve function references and snapshot boundaries ([84a17d5](https://github.com/morluto/rea/commit/84a17d55199b1b46493aea6bf4e41029a3b200fc))
|
||||
* **ghidra:** preserve POSIX runtime paths with spaces ([80c3d36](https://github.com/morluto/rea/commit/80c3d36dfa2597f34fdeb69e0ba75277ee50c85c))
|
||||
* **ghidra:** preserve source failures and snapshot ownership ([704fa76](https://github.com/morluto/rea/commit/704fa7699ca183d5d5e0518a6fcdbb953ec35a10))
|
||||
* **ghidra:** preserve Unicode boundaries without breaking sessions ([bef234d](https://github.com/morluto/rea/commit/bef234d6ce63cd296a654400443fcdbbcd44d7de))
|
||||
* **ghidra:** recover regex stack exhaustion without losing annotations ([bc4aa46](https://github.com/morluto/rea/commit/bc4aa46d7c3960a1f325523e48aed2089b176e84))
|
||||
* **ghidra:** reject address truncation before reads and edits ([dcae3c6](https://github.com/morluto/rea/commit/dcae3c6c148582d235f5650aace6f9744eeb153c))
|
||||
* **ghidra:** resolve exact external function entries ([549effa](https://github.com/morluto/rea/commit/549effa056729e5f9239d9ed7864537a9c49e421))
|
||||
* **hopper:** bind analysis to the active target and correct boundary contracts ([2f71375](https://github.com/morluto/rea/commit/2f71375efc517bb9e6f9800f8e8a6849766bc893))
|
||||
* **hopper:** bind file mappings to verified source bytes ([82626c4](https://github.com/morluto/rea/commit/82626c4499a53c227187575d40a845fadd5318e2))
|
||||
* **hopper:** diagnose MCP provider failures with stage, state, and request context ([#1044](https://github.com/morluto/rea/issues/1044)) ([5a67c7f](https://github.com/morluto/rea/commit/5a67c7fc540c494371de76dcdf353923656993d3))
|
||||
* **hopper:** enforce caller-supplied request deadlines ([#1119](https://github.com/morluto/rea/issues/1119)) ([e8939ca](https://github.com/morluto/rea/commit/e8939ca2208bc41454b73b90a32418714e6a2969))
|
||||
* **hopper:** isolate regex matching from the native API thread ([f201b04](https://github.com/morluto/rea/commit/f201b04fbbb29591b0d2bb3f3b24636526e4ec4e))
|
||||
* **hopper:** load verified FAT64 slices with native Mach-O semantics ([0a49d5b](https://github.com/morluto/rea/commit/0a49d5bfb931124d3d398dfbd64e9f6ae8a01405))
|
||||
* **hopper:** normalize native block endpoints and retain terminal evidence ([2847311](https://github.com/morluto/rea/commit/2847311a579ce2da4c8ded4d89fcdd01545e6da5))
|
||||
* **hopper:** preserve launcher evidence on readiness timeout ([458771c](https://github.com/morluto/rea/commit/458771cb583e38b848b3d8c58595d163143ff50a))
|
||||
* **hopper:** preserve native navigation and annotation semantics ([f2e22e1](https://github.com/morluto/rea/commit/f2e22e13a6956985bd65a2d75e4db7e3128cce81))
|
||||
* **hopper:** preserve owned basic-block endpoint instructions ([#1118](https://github.com/morluto/rea/issues/1118)) ([329cf20](https://github.com/morluto/rea/commit/329cf206d1111c051733c7115cabff4b46722deb))
|
||||
* **hopper:** preserve partial native call and string evidence ([2461702](https://github.com/morluto/rea/commit/246170238ebb8669a20b57b559b03cfc26ee1b8c))
|
||||
* **hopper:** preserve typed string values and verify real searches ([7d7b3f9](https://github.com/morluto/rea/commit/7d7b3f961af2dc9b7572e91aaec29e6c4eccf9d5))
|
||||
* **hopper:** report launcher failures before bridge timeout ([#1105](https://github.com/morluto/rea/issues/1105)) ([cd756b0](https://github.com/morluto/rea/commit/cd756b07d5b9c06e085d778e9f1c4fc9bd403219))
|
||||
* **hopper:** resolve FAT loaders and original file coordinates ([35f1ddd](https://github.com/morluto/rea/commit/35f1ddd6bc77588b9971961cd70968a815aae266))
|
||||
* **hopper:** validate annotation destinations and symbol extents ([b2d5bc1](https://github.com/morluto/rea/commit/b2d5bc110914f2ad42d17bc739d5f37c1b0f0a4d))
|
||||
* **hopper:** verify lease cleanup and repair the macOS test lane ([#1111](https://github.com/morluto/rea/issues/1111)) ([b743bd2](https://github.com/morluto/rea/commit/b743bd20a0cbc39728e2e822fa75c013d87e1730))
|
||||
* **inspector:** bound retained metadata and preserve cancellation ([e714fb0](https://github.com/morluto/rea/commit/e714fb027afdf11bfbb0838722523d3fa9ebf244))
|
||||
* **install:** guard empty prefix_args expansion on macOS Bash 3.2 under set -u ([#1061](https://github.com/morluto/rea/issues/1061)) ([68b9fa4](https://github.com/morluto/rea/commit/68b9fa489b0c07f580633785ec61c17fa20b5083))
|
||||
* **javascript:** bound primitive string growth and repeated evaluation ([4a1837d](https://github.com/morluto/rea/commit/4a1837d9d52b1a6454469e2dc63eeb8d716fb296))
|
||||
* **javascript:** bound semantic expansion and resolve lexical receivers ([16f4609](https://github.com/morluto/rea/commit/16f4609d689445c05a0befc744fce7e72f7047ec))
|
||||
* **javascript:** classify direct defaults and argv elements ([6495171](https://github.com/morluto/rea/commit/649517181268a0b729970b7cbbda15e04b199e32))
|
||||
* **javascript:** honor cancellation before evidence publication ([#1098](https://github.com/morluto/rea/issues/1098)) ([cc0ee2e](https://github.com/morluto/rea/commit/cc0ee2edc60dda42d7b7cfb2c08e39b0b736b5e9))
|
||||
* **javascript:** omit self-referential import edges with disclosure ([#1130](https://github.com/morluto/rea/issues/1130)) ([1d7115b](https://github.com/morluto/rea/commit/1d7115b7b470719a32e477ca3cbb681f0ad408b8))
|
||||
* **javascript:** parse plain TypeScript artifact dialects ([#1102](https://github.com/morluto/rea/issues/1102)) ([3db4681](https://github.com/morluto/rea/commit/3db46813f80f84504c5b87c73a4bece82a8fdc5e))
|
||||
* **javascript:** preserve HTML carriage return source ranges ([#1092](https://github.com/morluto/rea/issues/1092)) ([23862af](https://github.com/morluto/rea/commit/23862af1b57413e09abf394f6e4f8369f6355d5a))
|
||||
* **javascript:** preserve lexical open and exact template semantics ([d6cc9ca](https://github.com/morluto/rea/commit/d6cc9ca3a6e92604c837f61c193baf5abedaabc8))
|
||||
* **javascript:** preserve original BOM source coordinates ([#1093](https://github.com/morluto/rea/issues/1093)) ([83defc0](https://github.com/morluto/rea/commit/83defc054b69e94da888d70bb3b05d835e1bf74d))
|
||||
* **javascript:** retain NodeNext artifact source facts ([#1087](https://github.com/morluto/rea/issues/1087)) ([c1b370c](https://github.com/morluto/rea/commit/c1b370ce0dfcbd5725cf6aebe23b022dfc75ac84))
|
||||
* **javascript:** use Node legacy package entry fields ([1706005](https://github.com/morluto/rea/commit/170600541bf44f2a63731e8b3d42aae3cbb8d6b3))
|
||||
* **managed:** bind inspection evidence to artifact identity ([73b09f4](https://github.com/morluto/rea/commit/73b09f4c5c511ce80baa0c89717181bbe0ecba8f))
|
||||
* **managed:** match exact method bytes without decoded signatures ([17809ca](https://github.com/morluto/rea/commit/17809caacd0f44fcc7c1af42932ea655489d64ae))
|
||||
* **mcp:** flatten root union input schemas ([#1062](https://github.com/morluto/rea/issues/1062)) ([6a7650e](https://github.com/morluto/rea/commit/6a7650e93cba9169baad95d2c52b11351ae39d0e))
|
||||
* **mcp:** preserve retained evidence on oversized delivery ([0639a54](https://github.com/morluto/rea/commit/0639a541a3b712940119f59e8189bc962b267726))
|
||||
* **mcp:** retain complete application results across response limits ([a1ed423](https://github.com/morluto/rea/commit/a1ed4232de635d4acc0dd9eb293e309a29d03e80))
|
||||
* **mcp:** retain oversized errors before transport delivery ([b3f1b4e](https://github.com/morluto/rea/commit/b3f1b4e822aec5626d1952a88f50c7520dc265d3))
|
||||
* **metadata:** stop schema changes churning generated skill evidence ([#1075](https://github.com/morluto/rea/issues/1075)) ([d52f66d](https://github.com/morluto/rea/commit/d52f66d62a93ec09cccdf53e64e3ccd24d16e520))
|
||||
* **native:** bind signature observations to registered target versions ([#1099](https://github.com/morluto/rea/issues/1099)) ([c789af0](https://github.com/morluto/rea/commit/c789af04b76c0e76a522a5dece34f461288f5ecd))
|
||||
* **native:** bound retained observations and preserve failure evidence ([7c32387](https://github.com/morluto/rea/commit/7c323870c2dcab78654ee7a80128b06affdf2bbe))
|
||||
* **native:** preserve bridge failure types during cleanup ([1d0baa3](https://github.com/morluto/rea/commit/1d0baa352fc76d1760abbe29a3cc80fa17296916))
|
||||
* **native:** resample tool identity for each invocation ([14b19a0](https://github.com/morluto/rea/commit/14b19a0b8a5af401212c552173e761eac53bfe46))
|
||||
* **native:** share command supervision and retain failure output ([f2e9c49](https://github.com/morluto/rea/commit/f2e9c491ea00bf79d5665102d95a3339b63cfe4f))
|
||||
* **native:** supervise LLDB and preserve bounded capture uncertainty ([915855e](https://github.com/morluto/rea/commit/915855e75ca3fe3b1b54978ace6bbe10d1973f64))
|
||||
* **process:** avoid cold ownership helper deadline exhaustion ([#1139](https://github.com/morluto/rea/issues/1139)) ([65a809a](https://github.com/morluto/rea/commit/65a809a0da0555aa8eeafbc2ec2559517471be02))
|
||||
* **process:** derive executable names from the capture host ([#1108](https://github.com/morluto/rea/issues/1108)) ([34cc925](https://github.com/morluto/rea/commit/34cc9259c1076a0d9575a87679befca692ec86c2))
|
||||
* **process:** identify unknown live processes in cleanup diagnostics ([eb89a16](https://github.com/morluto/rea/commit/eb89a16d9bc51b65dcc75e05f8492de894320753))
|
||||
* **process:** name the reason two captures cannot be compared ([#1080](https://github.com/morluto/rea/issues/1080)) ([eb69763](https://github.com/morluto/rea/commit/eb69763d27e29fe3247c008925016e4c73cb6abe))
|
||||
* **process:** preserve observations after successful cleanup ([2af12a1](https://github.com/morluto/rea/commit/2af12a18a36bb80fb7d91ef4c8009212750daa0a))
|
||||
* **process:** retain ownership after incomplete cleanup ([e2fc249](https://github.com/morluto/rea/commit/e2fc2493b6a59f0663495d277c0cfec1aeb25a7a))
|
||||
* **process:** stabilize native capture and ownership prerequisites ([2b27515](https://github.com/morluto/rea/commit/2b2751547662f8cb142f96d91eff50388a1ecb31))
|
||||
* **process:** stop failing captures on tokens macOS cannot expose ([#1057](https://github.com/morluto/rea/issues/1057)) ([5547c1e](https://github.com/morluto/rea/commit/5547c1e697af8c2e1c771abe5344d10b7c1cd340))
|
||||
* **providers:** validate ELF versions and preserve probe cancellation ([8b01e0c](https://github.com/morluto/rea/commit/8b01e0cc8904b512b517b4471ef2150f40c98f0d))
|
||||
* **release:** always bump minor for releases ([be783ef](https://github.com/morluto/rea/commit/be783efaba62dc76c5d8d42229869a32afdb302a))
|
||||
* **release:** publish prereleases under the next npm tag ([bfb7776](https://github.com/morluto/rea/commit/bfb7776006303c6164dba2f22b8e81fdb7c20beb))
|
||||
* **release:** publish reviewed release PR merges ([cdddfdd](https://github.com/morluto/rea/commit/cdddfddf495ffd172f62a6a801f7697fe502847e))
|
||||
* **release:** recognize conventional titles in GitHub merge commits ([f5009ed](https://github.com/morluto/rea/commit/f5009ed55f081939409808870458162991f836ca))
|
||||
* **release:** restore automatic release proposals on main ([acb3e0f](https://github.com/morluto/rea/commit/acb3e0f2e5b38a9c97df7ed78c8e6e5f38bfa08f))
|
||||
* **release:** validate frozen checkpoint versions and breaking notes ([8a31508](https://github.com/morluto/rea/commit/8a31508c8958800364e255540ded1586298d7008))
|
||||
* **release:** validate frozen checkpoint versions and breaking notes ([60825fb](https://github.com/morluto/rea/commit/60825fb44751c9482bdf4cafdac3599da7c82d45)), closes [#1063](https://github.com/morluto/rea/issues/1063)
|
||||
* select Node legacy package entry fields for import and require ([#1035](https://github.com/morluto/rea/issues/1035)) ([1706005](https://github.com/morluto/rea/commit/170600541bf44f2a63731e8b3d42aae3cbb8d6b3))
|
||||
* **session:** serialize snapshot lifecycle with admitted requests ([e44cb45](https://github.com/morluto/rea/commit/e44cb45908af8d3902e0db044e0cf7b86cdd7ed0))
|
||||
* **setup:** launch Windows npx registrations through cmd.exe ([#1072](https://github.com/morluto/rea/issues/1072)) ([ef30343](https://github.com/morluto/rea/commit/ef30343154d64d78b620d41c37457eee5d99fc71))
|
||||
* **setup:** treat empty client config files as new documents ([#1117](https://github.com/morluto/rea/issues/1117)) ([9fd65ce](https://github.com/morluto/rea/commit/9fd65ce0711ce9257aee15e62a5ecf9832960035))
|
||||
* **snapshot:** retain MCP workflow replay bindings ([5e1afe4](https://github.com/morluto/rea/commit/5e1afe454f2f283eeeea4532d07a9bdbea8bd51e))
|
||||
* **target:** preserve app bundle filesystem failure reasons ([d4c2c3c](https://github.com/morluto/rea/commit/d4c2c3c18f691d04526fe38867347faa2530861d))
|
||||
* **update:** accept npm 12 release metadata ([#1138](https://github.com/morluto/rea/issues/1138)) ([567a82f](https://github.com/morluto/rea/commit/567a82f8d4feb629a7c10d2cc9180377eb1f6f66))
|
||||
* **verify:** close both sides of the browser proxy ([f7366a5](https://github.com/morluto/rea/commit/f7366a517ff0545f01df6202b1ed3231d9dd3b4a))
|
||||
* **verify:** isolate package archives and published canary caches ([3eb7dbd](https://github.com/morluto/rea/commit/3eb7dbdc1689737b44cc4ff044bcb1a311511969))
|
||||
* **verify:** remove stale mocked bridge package probe ([826583d](https://github.com/morluto/rea/commit/826583dd43fc918c70a87dfe148c2fdbc8612f3b))
|
||||
* **verify:** report final success only after resource cleanup ([ee043e2](https://github.com/morluto/rea/commit/ee043e2cbf30152c60b403fff050a0fc848a3286))
|
||||
* **workflows:** keep caller-input details in workflow input errors ([#1089](https://github.com/morluto/rea/issues/1089)) ([289aef6](https://github.com/morluto/rea/commit/289aef6838e424c14f331a9905020079e6b7539b))
|
||||
* **workflows:** retain residual unknowns through CLI and MCP ([0136ee8](https://github.com/morluto/rea/commit/0136ee8c22300ed351af5eab6329ab4d271b5bb6))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **mcp:** advertise each tool JSON Schema once per target ([#1059](https://github.com/morluto/rea/issues/1059)) ([5ccb924](https://github.com/morluto/rea/commit/5ccb9241be15bb6a19b99fbbfb27ff3356ba157b))
|
||||
* **build:** avoid duplicate documentation generation ([c701376](https://github.com/morluto/rea/commit/c701376777212ec9acfd10c97f45b1eaf0c885c6))
|
||||
* **javascript:** index configuration and module lookups ([67252b2](https://github.com/morluto/rea/commit/67252b23b9e35e9526fed7842dab47a2c7c9fe5f))
|
||||
* **javascript:** reuse validated immutable graphs ([#1112](https://github.com/morluto/rea/issues/1112)) ([ec6db0f](https://github.com/morluto/rea/commit/ec6db0f0630c4b0b35f0db84c0030784c1aa58a6))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **artifacts:** centralize stable inventory identities ([135d102](https://github.com/morluto/rea/commit/135d102f9df844d70d352870afce80f67c0ae96f))
|
||||
* **browser:** charge source-map records before retention ([e762e32](https://github.com/morluto/rea/commit/e762e32afbba3fd32532fd42a3f4fdcd05252ad3))
|
||||
* **config:** prune unused format wrappers and launcher export ([81ae838](https://github.com/morluto/rea/commit/81ae8385679ef10b95d34d0f0b5ba11732ac86b5))
|
||||
* **input:** share schema issue error construction ([65a6288](https://github.com/morluto/rea/commit/65a6288257e4c3cab9adb53b0f9123ef567b61bd))
|
||||
* **setup:** validate formats through canonical configuration ([4b10665](https://github.com/morluto/rea/commit/4b1066501b379107b2375e270f386fc9855a85c1))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add Persian README translation with full language navigation ([7a7460f](https://github.com/morluto/rea/commit/7a7460f04f3cfd7e365d70ff891bdd0c474901b6))
|
||||
* add ten README translations and expand language navigation ([a61491a](https://github.com/morluto/rea/commit/a61491ad00b21e912e16f68bff829927aed6e16f))
|
||||
* align translated READMEs and website links with English ([d83f098](https://github.com/morluto/rea/commit/d83f0983d5aa18d29d95d24c4ccb3928be6f29f5))
|
||||
* celebrate 20,000 GitHub stars 🎉 ([#1079](https://github.com/morluto/rea/issues/1079)) ([9455e7c](https://github.com/morluto/rea/commit/9455e7c8bc9d5c4a9a1fc9077aa15007ad80997e))
|
||||
* check surrounding cases when changing boundaries ([46d195e](https://github.com/morluto/rea/commit/46d195e8a48b710fc355e84cb6b79461b9c39005))
|
||||
* clarify contribution scope and cosmetic suggestions ([1f79002](https://github.com/morluto/rea/commit/1f7900249783956070af49c10f86002a0dfdc42b))
|
||||
* clarify evidence ownership and verification guidance ([a9e00b8](https://github.com/morluto/rea/commit/a9e00b83f37651a8dd241375615b248062e04227))
|
||||
* move Portuguese toward the end of language navigation ([2a4979b](https://github.com/morluto/rea/commit/2a4979b7cdc1a517865573d6c36654c022347d0a))
|
||||
* move star milestone into history sections across readmes ([6e6ff7b](https://github.com/morluto/rea/commit/6e6ff7bd2eabb668602258ad5e31eb758b31f823))
|
||||
* require partial native facts at provider boundaries ([2b86ea9](https://github.com/morluto/rea/commit/2b86ea9c7cd3cfc2141a021f8b531dbc84395c81))
|
||||
* require surrounding boundary checks for review fixes ([9833c94](https://github.com/morluto/rea/commit/9833c94656e1d54e7711dc483bf8091ced14234f))
|
||||
* **testing:** prioritize real workflows and distinct regressions ([9588482](https://github.com/morluto/rea/commit/9588482bfeeea869d7274d27fb96bac70278dd42))
|
||||
* **website:** add a CTF showcase and FAQ ([#1077](https://github.com/morluto/rea/issues/1077)) ([a4d77f0](https://github.com/morluto/rea/commit/a4d77f0ff0242b7ab891030d2ffa09a4b4bc1c3c))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **acceptance:** add application workflow MCP parity harness and scenarios ([5ac3dc9](https://github.com/morluto/rea/commit/5ac3dc96f064e691b357aeddc11fa76b7b4d66d1))
|
||||
* **artifacts:** retire simulated mounts after detach failures ([f591e37](https://github.com/morluto/rea/commit/f591e37a1e45d9b7179ed788c4e7ee77a708c6b0))
|
||||
* **artifacts:** verify native directory identity on supported Windows runtimes ([#1069](https://github.com/morluto/rea/issues/1069)) ([e94be95](https://github.com/morluto/rea/commit/e94be95c66515fa6fba2a46e4b29850d34141fef))
|
||||
* **browser:** pin DOM and source-map URL policy classifications ([#1120](https://github.com/morluto/rea/issues/1120)) ([c1d0002](https://github.com/morluto/rea/commit/c1d0002470dc2b6bde73e4ffe438e734dbb24d8d))
|
||||
* **browser:** reject unmodeled CDP commands in protocol fixtures ([017297c](https://github.com/morluto/rea/commit/017297c40c0c38b9d9c17f5194f0f4c51748f0c3))
|
||||
* classify application scenarios by their actual boundaries ([0e0b386](https://github.com/morluto/rea/commit/0e0b386dc35bdee1174df9a2d718e0f3e5793666))
|
||||
* **cli:** verify self-import parity and cleanup ([55c9ce2](https://github.com/morluto/rea/commit/55c9ce26d1b52f359a21512103088048f9aa7492))
|
||||
* consolidate failure guards and trim unused fixture APIs ([ffd9611](https://github.com/morluto/rea/commit/ffd9611308c91b894a7b0d27f331238f5117dacf))
|
||||
* **hopper:** cover real boundary failures and CLI parity ([64717ad](https://github.com/morluto/rea/commit/64717ad7daa3152eeb718b4eb5fdcc77f1d76542))
|
||||
* **hopper:** verify real boundaries and prune redundant happy paths ([7005166](https://github.com/morluto/rea/commit/7005166f5878737fb7b3c7ec4e3d2ee5f4dcce30))
|
||||
* **mcp:** serialize real process capture and report unavailable authority ([8f9da25](https://github.com/morluto/rea/commit/8f9da251647beb530f6851c9dad5040b19a50a35))
|
||||
* **process:** cover argv rewrites in native ownership inspection ([09d7b9b](https://github.com/morluto/rea/commit/09d7b9bab53ae968502fd6e19df694cca8cab817))
|
||||
* **process:** distinguish native startup from producer verification ([9a70fb4](https://github.com/morluto/rea/commit/9a70fb482c974658088167c9d279bd70159e5dc8))
|
||||
* **process:** expose unavailable capture and unexpected teardown failures ([90b06db](https://github.com/morluto/rea/commit/90b06db6c0b2c5863f292f63a226a5c5678a86ac))
|
||||
* **process:** report the cause of terminal capture failures ([8a4c4b6](https://github.com/morluto/rea/commit/8a4c4b657fe3b0c249dd2e2147f6960361684c59))
|
||||
* **process:** retain cancellation checks under unverified host cleanup ([9a5b077](https://github.com/morluto/rea/commit/9a5b0778993102460e333de6cad3ba85383c10be))
|
||||
* **process:** retire native caches at worker teardown ([a0784c0](https://github.com/morluto/rea/commit/a0784c0d2cb70c48ecfeb0be23c6d68de5ee5be1))
|
||||
* **process:** serialize real CLI capture boundaries ([82560eb](https://github.com/morluto/rea/commit/82560ebf593a7dfc704698ee312e4d8f1629be8f))
|
||||
* prune redundant cases and promote configuration lifecycle coverage ([5945915](https://github.com/morluto/rea/commit/5945915f91adf85de91e16b69534bc3ed1d1b69a))
|
||||
* prune resource-recovery wording assertions ([ae06dcf](https://github.com/morluto/rea/commit/ae06dcf83f65b06abb390d2e0b0ea33543278d6e))
|
||||
* remove duplicate result assertions before throwing guards ([a1b7d40](https://github.com/morluto/rea/commit/a1b7d40bb0212cdfdddd770a1bccbed097601e0a))
|
||||
* **session:** preserve live binding after cancelled switches ([1136ea8](https://github.com/morluto/rea/commit/1136ea881029eab59ffb0834149e86a6aff69e3b))
|
||||
* **setup:** scope removal regression to its fixture client ([fe48e77](https://github.com/morluto/rea/commit/fe48e779c264745d28b5a819e51c06e6b5bd2776))
|
||||
* **workflows:** verify residual questions in CLI snapshots ([770d2f6](https://github.com/morluto/rea/commit/770d2f60aa948cfdb61c71304be2f3e35f673840))
|
||||
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
* run signature target binding regressions on native macOS ([#1107](https://github.com/morluto/rea/issues/1107)) ([1e63144](https://github.com/morluto/rea/commit/1e63144390070f569998271fafd1459788ef3265))
|
||||
|
||||
## [6.0.0](https://github.com/morluto/rea/compare/rea-agents-5.0.0...rea-agents-6.0.0) (2026-10-08)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **contracts:** MCP filesystem inputs now require absolute host paths, including target/snapshot paths, evidence bundle paths, managed and firmware inputs, browser/Electron launch paths and expected server paths. Calls such as `open_binary({"path":"./app"})` must send an absolute path such as `/home/analyst/app` or `C:\analysis\app`. Optional paths remain optional. CLI operator-relative paths continue to work. See [#948](https://github.com/morluto/rea/pull/948) and [#1004](https://github.com/morluto/rea/pull/1004).
|
||||
|
||||
### Features
|
||||
|
||||
* **artifacts:** trace Mach-O dylib resolution in bundles ([78d594c](https://github.com/morluto/rea/commit/78d594cacd376725f833a99402069c103aae9aac))
|
||||
* **browser:** inspect historical web network captures ([#992](https://github.com/morluto/rea/issues/992)) ([2bf3663](https://github.com/morluto/rea/commit/2bf3663f654386c9fd8c1437c49659df97bd4697))
|
||||
* **evm:** inspect offline bytecode interfaces through EVMole ([#1051](https://github.com/morluto/rea/issues/1051)) ([7af7bc8](https://github.com/morluto/rea/commit/7af7bc88ce0e8f084eaa0db1e8f834d495440fd9))
|
||||
* **ghidra:** support native x86 PE applications on Windows ([#1030](https://github.com/morluto/rea/issues/1030)) ([c6dad93](https://github.com/morluto/rea/commit/c6dad932084032bcb32acc61e8066a7784497710))
|
||||
* **native:** inspect offline ELF layout through pwntools ([#1037](https://github.com/morluto/rea/issues/1037)) ([4fc6565](https://github.com/morluto/rea/commit/4fc6565b0ffb36d7105b3524e08d0c5d603ae07d))
|
||||
* **native:** inspect recorded crashes through pwntools and pwndbg ([#1054](https://github.com/morluto/rea/issues/1054)) ([163ee09](https://github.com/morluto/rea/commit/163ee09a7425479385e0ed0b5d74043e2587eab2))
|
||||
* **native:** observe function and Objective-C method calls under LLDB ([1d13cef](https://github.com/morluto/rea/commit/1d13cef5f46f5e07f594bde7b1a4725f189b9a5e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dotnet:** validate managed metadata and CIL boundaries ([#938](https://github.com/morluto/rea/pull/938)) ([bda4ada](https://github.com/morluto/rea/commit/bda4ada7fb6bd7dd70a444bb3c57e34f7f5cc00b))
|
||||
|
||||
* **build:** clarify generated-file check hint and drop redundant catalog step ([#998](https://github.com/morluto/rea/pull/998)) ([6f0ef16](https://github.com/morluto/rea/commit/6f0ef160501ea3b24dfa2939dfcd3eea0dc13ee7))
|
||||
|
||||
* **release:** extend npm propagation window and improve diagnostics ([#943](https://github.com/morluto/rea/pull/943)) ([06b047a](https://github.com/morluto/rea/commit/06b047a0929369d5b8635b3651da115c8fcd53a7))
|
||||
|
||||
* **contracts:** require absolute local paths for session filesystem inputs ([#948](https://github.com/morluto/rea/pull/948)) ([5b172b1](https://github.com/morluto/rea/commit/5b172b1042fc09a4e12d956018f15d86cf397ffe))
|
||||
|
||||
* **ci:** generate source catalog before shard tests ([#1002](https://github.com/morluto/rea/pull/1002)) ([eb8f649](https://github.com/morluto/rea/commit/eb8f6491a88bfa79d13f7923e4ef94d791f6ba4d))
|
||||
|
||||
* **contracts:** require absolute local paths for remaining caller-supplied file inputs ([#1004](https://github.com/morluto/rea/pull/1004)) ([cbc0dd1](https://github.com/morluto/rea/commit/cbc0dd1e7ca36fa3504a9ea8eae303a0876b176b))
|
||||
|
||||
* **cli:** classify ENOTDIR JSON input failures ([#1000](https://github.com/morluto/rea/pull/1000)) ([a6132be](https://github.com/morluto/rea/commit/a6132be0546d6a9e3fa83426c8b59b1280b3e5bd))
|
||||
|
||||
* **browser:** preserve authorized redirect hops ([#986](https://github.com/morluto/rea/pull/986)) ([ae0f725](https://github.com/morluto/rea/commit/ae0f725fad368730c6198d49716ccc410bca0536))
|
||||
|
||||
* **mcp:** reject unknown MCP tool inputs ([#975](https://github.com/morluto/rea/pull/975)) ([fe64fd6](https://github.com/morluto/rea/commit/fe64fd65d93eff202ad90145544dc9ff84651974))
|
||||
|
||||
* **release:** publish from explicit source checkpoints ([#1003](https://github.com/morluto/rea/pull/1003)) ([64c7188](https://github.com/morluto/rea/commit/64c71880ae4f7bc658ffce46eb729c4141dbdf19))
|
||||
|
||||
* **release:** publish from explicit source checkpoints ([#1006](https://github.com/morluto/rea/pull/1006)) ([b4fc0ec](https://github.com/morluto/rea/commit/b4fc0ecaa1f6b912dc27880e6b6127ace47abc2e))
|
||||
|
||||
* **javascript:** bound semantic node projection for resource safety ([#966](https://github.com/morluto/rea/pull/966)) ([96a46ad](https://github.com/morluto/rea/commit/96a46ada0a36f0349f2b0fc20b4c932401b4ad84))
|
||||
|
||||
* **ci:** avoid slow Azure archive for the native cross compiler ([#1016](https://github.com/morluto/rea/issues/1016)) ([51956c1](https://github.com/morluto/rea/commit/51956c1a49a7dc9ed70a6fba480a670a7e7b2b9b))
|
||||
* **ci:** decouple stdio smoke tests from Linux display probes ([#1014](https://github.com/morluto/rea/issues/1014)) ([93731e1](https://github.com/morluto/rea/commit/93731e17ddea76db86dfa84bb54536dcf8ebfa0b))
|
||||
* **cli:** stream large JavaScript JSON results ([#1024](https://github.com/morluto/rea/issues/1024)) ([0e25bef](https://github.com/morluto/rea/commit/0e25befe78f220691abe5d0291cc04a75c9cc877))
|
||||
* **javascript:** complete large-ASAR analysis with file-local projection ([#1055](https://github.com/morluto/rea/issues/1055)) ([b9fb438](https://github.com/morluto/rea/commit/b9fb43883570bef7cb29fd16f27c25d5a7e5bb54))
|
||||
* **javascript:** recognize digits in artifact URI schemes ([#1045](https://github.com/morluto/rea/issues/1045)) ([03424ae](https://github.com/morluto/rea/commit/03424ae91ef22efd57db1bdde9379a3ac450b8f7))
|
||||
* **javascript:** reconstruct HTML script loads accurately ([#1042](https://github.com/morluto/rea/issues/1042)) ([1154f2f](https://github.com/morluto/rea/commit/1154f2f03c0b0bb6cb6c5da09b8d8e1c837febc4))
|
||||
* **javascript:** report application analysis progress on the CLI ([#1008](https://github.com/morluto/rea/issues/1008)) ([7b18c58](https://github.com/morluto/rea/commit/7b18c581409848077de7a9cb6d7d1a0b91a02e12))
|
||||
* **javascript:** resolve module URLs and reject invalid exports targets ([#1031](https://github.com/morluto/rea/issues/1031)) ([8d5c88f](https://github.com/morluto/rea/commit/8d5c88f17cde4ab4fa54c5142f0a45e612619a53))
|
||||
* **javascript:** resolve root package exports arrays ([#1046](https://github.com/morluto/rea/issues/1046)) ([53409ab](https://github.com/morluto/rea/commit/53409ab1968d60c77f98feedefe237a08859a95c))
|
||||
* **mcp:** advertise JSON values without recursive schemas ([#995](https://github.com/morluto/rea/issues/995)) ([7425f70](https://github.com/morluto/rea/commit/7425f70fd0247a7ea7192deb959fd6f48ba83a21)), closes [#919](https://github.com/morluto/rea/issues/919)
|
||||
* **mcp:** bound capture comparison input schema depth ([#1005](https://github.com/morluto/rea/issues/1005)) ([6b55de0](https://github.com/morluto/rea/commit/6b55de017badc9066642e1ff2561eb1da0ff93f8))
|
||||
* **process:** preserve partial evidence and verify detached cleanup ([#1011](https://github.com/morluto/rea/issues/1011)) ([63cf28f](https://github.com/morluto/rea/commit/63cf28fd8f8acaf656e993300a2a24280b2fd692))
|
||||
* **release:** support built-in credentials and refresh release boundary ([6a278a6](https://github.com/morluto/rea/commit/6a278a68dd4fa69b9153697ecb7bba7de4c55c32))
|
||||
* **release:** support default credentials and record 5.0.0 boundary ([#1012](https://github.com/morluto/rea/issues/1012)) ([6a278a6](https://github.com/morluto/rea/commit/6a278a68dd4fa69b9153697ecb7bba7de4c55c32))
|
||||
* **release:** sync 5.0.0 checkpoint back to main ([966cca8](https://github.com/morluto/rea/commit/966cca8efe5ed08b3285cd06409688d871eb5e6b))
|
||||
* **snapshot:** replay validated workflow evidence ([#984](https://github.com/morluto/rea/issues/984)) ([1d62e54](https://github.com/morluto/rea/commit/1d62e54e1f976e64130e4e9b651c3180aa4fa727))
|
||||
* **windows:** allow Java to read retained target snapshots ([#1027](https://github.com/morluto/rea/issues/1027)) ([28d5c39](https://github.com/morluto/rea/commit/28d5c397c83f901e4086083868041711371ad32a))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **artifacts:** replace quadratic ZIP overlap checks ([#1013](https://github.com/morluto/rea/issues/1013)) ([76e7e51](https://github.com/morluto/rea/commit/76e7e510bb2d7d37728ca50749d524beb5890cf8))
|
||||
* **javascript:** cache canonical export comparison sort keys ([#1028](https://github.com/morluto/rea/issues/1028)) ([63ba742](https://github.com/morluto/rea/commit/63ba7424c0ab69b94397e8913f8e231de111583f))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **build:** generate MCP tool catalog at build time instead of committing it ([#997](https://github.com/morluto/rea/pull/997)) ([35a4e18](https://github.com/morluto/rea/commit/35a4e18d3028014e24d2e209ec99a590fde63152))
|
||||
|
||||
* **process:** separate capture implementation from analyst workflows ([#988](https://github.com/morluto/rea/pull/988)) ([6aa9afb](https://github.com/morluto/rea/commit/6aa9afb5493a97801eb1126a0dcf11f9705165db))
|
||||
|
||||
* **artifacts:** move inventory and extraction into provider owners ([#1032](https://github.com/morluto/rea/issues/1032)) ([6e4535c](https://github.com/morluto/rea/commit/6e4535c1011f1982e9ab07b7ebcd600c19cd494d))
|
||||
* remove abandoned test-only analysis scaffolding ([c93422a](https://github.com/morluto/rea/commit/c93422a898fdbedb7d28709144b4337a2669440b))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* improve Korean wording and particles ([#1047](https://github.com/morluto/rea/issues/1047)) ([c5d165b](https://github.com/morluto/rea/commit/c5d165bd03fed95a0b0105661d3ba7e0efbfa449))
|
||||
* simplify the English README and organize guides ([#1043](https://github.com/morluto/rea/issues/1043)) ([28f10cf](https://github.com/morluto/rea/commit/28f10cf2bc7b7c590700b50d47072015f3e3c66e))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **process:** wait for observable snapshot ctime changes ([#999](https://github.com/morluto/rea/pull/999)) ([161ed91](https://github.com/morluto/rea/commit/161ed9157134d5c3b0fbae19ceb756ac58a26c04))
|
||||
|
||||
* **browser:** consolidate CDP workflows and colocate parser coverage ([2682904](https://github.com/morluto/rea/commit/26829045d0f0e744bb60d6babc2dc3312f4bfa53))
|
||||
* **ci:** stabilize browser source identity fixture delivery ([#1023](https://github.com/morluto/rea/issues/1023)) ([ed725da](https://github.com/morluto/rea/commit/ed725dace4a689e4e5c804dc6bf06c7fc663c7c9))
|
||||
* **cli:** reduce repeated startup and filesystem setup ([d88d6c1](https://github.com/morluto/rea/commit/d88d6c1897740ccbd6c57f4ba01861fc751fb3a8))
|
||||
* consolidate service and process fixtures ([f8a53a8](https://github.com/morluto/rea/commit/f8a53a8c065f1ccbdaa051a4e1ef267a2047d759))
|
||||
* **javascript:** consolidate source analysis and export regressions ([f517a5a](https://github.com/morluto/rea/commit/f517a5a1d7c0766ed50f8dfd5ee69e65525ac744))
|
||||
* **javascript:** cover plain long-line loader identities ([#1025](https://github.com/morluto/rea/issues/1025)) ([984fc27](https://github.com/morluto/rea/commit/984fc27ce19d57f708bdae688a258cf8663cdea6))
|
||||
* **mcp:** consolidate SDK schema checks and scenario ports ([9c3479e](https://github.com/morluto/rea/commit/9c3479ed95100d8ee5ed4d9158a4550e4ac2a752))
|
||||
* **providers:** verify framing through client socket boundaries ([1fd1989](https://github.com/morluto/rea/commit/1fd1989d342375a01b6d14b282ad58001b7d1800))
|
||||
* prune redundant conformance checks and document test priorities ([aafdf8a](https://github.com/morluto/rea/commit/aafdf8a01b42fb6fd927c0f2a66e758e1a510abc))
|
||||
|
||||
## [5.0.0](https://github.com/morluto/rea/compare/rea-agents-4.1.0...rea-agents-5.0.0) (2026-10-07)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **native:** preserve incomplete native UI captures ([#991](https://github.com/morluto/rea/issues/991))
|
||||
* **android:** Android analysis now requires a full JDK 17 or newer with its compiler module for the packaged Java source bridge.
|
||||
|
||||
### Features
|
||||
|
||||
* **apple:** project macOS bundle anatomy in application graphs ([88c940b](https://github.com/morluto/rea/commit/88c940b165d788a491e212ea93e00462c4dde6a2))
|
||||
* **apple:** project macOS bundle anatomy in application graphs ([dd3e7ae](https://github.com/morluto/rea/commit/dd3e7aef0d52c478f6cdf475501c42ebe7f5c97e))
|
||||
* **application:** wire Android and Apple inventory graph tools ([#852](https://github.com/morluto/rea/issues/852)) ([e0fd820](https://github.com/morluto/rea/commit/e0fd8207a1fb738bcc34f204b55c4fa154f5f02d))
|
||||
* **browser:** attribute native execution and listener sources ([#917](https://github.com/morluto/rea/issues/917)) ([b5afa5f](https://github.com/morluto/rea/commit/b5afa5f91eed9ae31d670aeab9bbf5112d790ec2))
|
||||
* **browser:** capture correlated network content and lifecycle evidence ([#797](https://github.com/morluto/rea/issues/797)) ([f9eaf05](https://github.com/morluto/rea/commit/f9eaf056ef58215094513176a70fabae0740081f))
|
||||
* **browser:** trace captured website module imports ([#879](https://github.com/morluto/rea/issues/879)) ([01be082](https://github.com/morluto/rea/commit/01be082e9e11c78b91828c06a9c543c754783c07))
|
||||
* **browser:** trace captured website source-map locations ([#897](https://github.com/morluto/rea/issues/897)) ([f430c41](https://github.com/morluto/rea/commit/f430c416c72d444fbe521e0263f8935da4cdb040))
|
||||
* **javascript:** recover readable modules with Wakaru ([#848](https://github.com/morluto/rea/issues/848)) ([166dcd2](https://github.com/morluto/rea/commit/166dcd21d06f687a9c480a6a6d36beb3b4954dd0))
|
||||
* **native:** decode chained fixups, binds, ObjC categories and properties ([a657557](https://github.com/morluto/rea/commit/a65755728f7a10d984514c75fa863b84ddcdaee9))
|
||||
* **native:** decode chained fixups, binds, ObjC categories and properties ([3e24a72](https://github.com/morluto/rea/commit/3e24a72465fe6c628db1f836e93d74d7f4a92b80))
|
||||
* **native:** open iOS-style and iOS-on-Mac wrapper app bundles ([2ed89e6](https://github.com/morluto/rea/commit/2ed89e6529924248d2e21084f8730bedbb16bc3f))
|
||||
* **native:** open iOS-style and iOS-on-Mac wrapper app bundles ([aeebba3](https://github.com/morluto/rea/commit/aeebba391ee5c288b75cd828e7d18d32b40aa698))
|
||||
* **web:** export captured scripts for JavaScript application analysis ([#801](https://github.com/morluto/rea/issues/801)) ([472b72a](https://github.com/morluto/rea/commit/472b72a53068a8e2ec78fe239d687023482daaa7))
|
||||
* **website:** add a shared back-to-top link ([5047373](https://github.com/morluto/rea/commit/504737300632d8644fbd6c658d2c97e38bb54c3b))
|
||||
* **website:** introduce REA and the DX-Ball reconstruction ([#906](https://github.com/morluto/rea/issues/906)) ([4e3953c](https://github.com/morluto/rea/commit/4e3953cc45ce9c21786a7a6fc495d2350aa85d24))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* address review nits from batch integration follow-ups ([#818](https://github.com/morluto/rea/issues/818)) ([5a0e0ba](https://github.com/morluto/rea/commit/5a0e0ba3b81ea0ec336addb07da98ec7bc0a35d3))
|
||||
* **android:** align runtime suffix inference ([#977](https://github.com/morluto/rea/issues/977)) ([fb3749a](https://github.com/morluto/rea/commit/fb3749aff692a7a8225dff00a4516b48a76866b6))
|
||||
* **android:** bound fragmented JADX MCP frame memory ([#937](https://github.com/morluto/rea/issues/937)) ([9e39795](https://github.com/morluto/rea/commit/9e397958951edb543134e3ad57e7ab253d6df21a))
|
||||
* **android:** preserve JVM settings and reuse metadata sessions ([#908](https://github.com/morluto/rea/issues/908)) ([eab34a9](https://github.com/morluto/rea/commit/eab34a9d8c5eebad6b04b85750be0276d679c3f9))
|
||||
* **apple:** attribute only application-root components in macOS inventories ([5524970](https://github.com/morluto/rea/commit/55249704c5c83cbf8d20ac7869863cc196da00a6))
|
||||
* **apple:** count only real framework version directories ([2e81e86](https://github.com/morluto/rea/commit/2e81e867b24da8314f666f62d154de5803040cdf))
|
||||
* **apple:** pair bridge hypotheses within one application root ([7acf4e7](https://github.com/morluto/rea/commit/7acf4e741a6a314ea7aabb18fc757e196158ff76))
|
||||
* **apple:** pair bridge hypotheses within one application root ([9226ade](https://github.com/morluto/rea/commit/9226ade5114f287eb8e1b00b08dd91da1cc66003))
|
||||
* **apple:** traverse keyed archive hierarchy collections ([#976](https://github.com/morluto/rea/issues/976)) ([71ac83f](https://github.com/morluto/rea/commit/71ac83f3dd376135098f460c86e7897183456ec5))
|
||||
* **artifacts:** accept APFS image disks ejected with their container ([d93ebec](https://github.com/morluto/rea/commit/d93ebec9df951d40e66ced667f07fadd2dd5d25a))
|
||||
* **artifacts:** accept APFS image disks ejected with their container ([b72c079](https://github.com/morluto/rea/commit/b72c079a9a90c374bd6dd30ff8c4387306b8f59b))
|
||||
* **artifacts:** accept the bundle subject when extracting an app bundle ([#891](https://github.com/morluto/rea/issues/891)) ([37d2eba](https://github.com/morluto/rea/commit/37d2ebac4c2a524eec6e32c4c57cbbb632491ad8))
|
||||
* **artifacts:** decode flat keyed-archive NIBs with data and shared references ([b3f013f](https://github.com/morluto/rea/commit/b3f013fa183e8351319a053575aeee5df337c276))
|
||||
* **artifacts:** decode flat keyed-archive NIBs with data and shared references ([2a4667b](https://github.com/morluto/rea/commit/2a4667bb79117b202abb009c0bb9ebfdfde57158))
|
||||
* **artifacts:** harden reader lifecycle boundaries ([#951](https://github.com/morluto/rea/issues/951)) ([2366bda](https://github.com/morluto/rea/commit/2366bda37b1084016d42a0d06920dc57e7c7723f))
|
||||
* **artifacts:** keep the post-snapshot detach check out of provenance ([62634e4](https://github.com/morluto/rea/commit/62634e4c0177bde8a08decc356da98fbe25d2000))
|
||||
* **artifacts:** preserve reader failure diagnostics ([#987](https://github.com/morluto/rea/issues/987)) ([fc4a9a0](https://github.com/morluto/rea/commit/fc4a9a05922ce8474fd0717bc800343b0a95bd41))
|
||||
* **artifacts:** project compiled AppKit actions from the control to its target ([b08228d](https://github.com/morluto/rea/commit/b08228d19f8a0f6dbc940e8d322ef2bb42d2ce1a))
|
||||
* **artifacts:** project compiled AppKit actions from the control to its target ([#905](https://github.com/morluto/rea/issues/905)) ([900d7c8](https://github.com/morluto/rea/commit/900d7c8606cbc1ccbca818827a55bf2eab28bca4))
|
||||
* **artifacts:** project UIKit runtime outlet and event connections ([aa8082e](https://github.com/morluto/rea/commit/aa8082ea6c0898feb4d842135aea6b9539f769f8))
|
||||
* **artifacts:** project UIKit runtime outlet and event connections ([8b24387](https://github.com/morluto/rea/commit/8b243877c22a5550d005e7b8abf93791ce8566b9))
|
||||
* **artifacts:** read AppKit connectors from flat keyed-archive NIBs ([#900](https://github.com/morluto/rea/issues/900)) ([27c272b](https://github.com/morluto/rea/commit/27c272bd9a42e7235c7991881196c60dbc126714))
|
||||
* **artifacts:** read keyed archive dictionaries keyed UID as dictionaries ([cfd2b0c](https://github.com/morluto/rea/commit/cfd2b0c75dfb9d51b936788a46d0338145803ece))
|
||||
* **artifacts:** read keyed archive dictionaries keyed UID as dictionaries ([35c82b9](https://github.com/morluto/rea/commit/35c82b93eb2579687bb446c3a8a2a9f1a7563d28))
|
||||
* **artifacts:** report which path constraint failed ([#847](https://github.com/morluto/rea/issues/847)) ([3f1a77f](https://github.com/morluto/rea/commit/3f1a77f68ec4355d8b792ccb2970bc03485889c9))
|
||||
* **browser:** distinguish computed call keys from identifiers ([#808](https://github.com/morluto/rea/issues/808)) ([02beb74](https://github.com/morluto/rea/commit/02beb745358f02bdaabed4bb523c5e3527a9841e))
|
||||
* **browser:** distinguish WebMCP registration frame owners ([#813](https://github.com/morluto/rea/issues/813)) ([b3b44d8](https://github.com/morluto/rea/commit/b3b44d8a2d8b2aa70a8ed8b9cddf365865fcc53f))
|
||||
* **browser:** fingerprint cookies at the selected page URL ([#763](https://github.com/morluto/rea/issues/763)) ([78014e3](https://github.com/morluto/rea/commit/78014e3d149f762c848581a0aac7e44f75d51225))
|
||||
* **browser:** identify NodeNext TypeScript original source artifacts ([#811](https://github.com/morluto/rea/issues/811)) ([e84ad86](https://github.com/morluto/rea/commit/e84ad86a7578fb46568663aca6fe6a6f3986cc8d))
|
||||
* **browser:** preserve evidence through deep AST analysis ([#924](https://github.com/morluto/rea/issues/924)) ([f826aa8](https://github.com/morluto/rea/commit/f826aa893fa7d55e87d907f0c94c39421a166dd8))
|
||||
* **browser:** preserve form destinations and satisfies mutation uncertainty ([#825](https://github.com/morluto/rea/issues/825)) ([6dd719e](https://github.com/morluto/rea/commit/6dd719eaf605b37cd5dad6d01d33527ca9d59f06)), closes [#820](https://github.com/morluto/rea/issues/820)
|
||||
* **browser:** preserve Link header value delimiters ([#805](https://github.com/morluto/rea/issues/805)) ([dd36405](https://github.com/morluto/rea/commit/dd3640548fe3d1e8b92784a63fac55d757dcc1f0))
|
||||
* **build:** validate installed dependencies before cache hits ([#982](https://github.com/morluto/rea/issues/982)) ([a45fe86](https://github.com/morluto/rea/commit/a45fe86a39b9e7a7c21e3665b6de4763cb72d782))
|
||||
* capture IndexedDB object-store record fingerprints ([#809](https://github.com/morluto/rea/issues/809)) ([21a8bd0](https://github.com/morluto/rea/commit/21a8bd0ccde2326c8bc022714a88a438d8b89dde))
|
||||
* **cli:** align structured output guard with native flags ([#777](https://github.com/morluto/rea/issues/777)) ([e9fac56](https://github.com/morluto/rea/commit/e9fac56232e74c8287863ca5c4cc7d8e39fb4a40))
|
||||
* **cli:** emit empty structured filter projections ([#914](https://github.com/morluto/rea/issues/914)) ([2db668f](https://github.com/morluto/rea/commit/2db668f6ca91d2514a354fd459239b84031812e7))
|
||||
* **cli:** honor the selected native plist path ([#773](https://github.com/morluto/rea/issues/773)) ([0374865](https://github.com/morluto/rea/commit/0374865836aa4960657fb92f588c71e35dd3bed8))
|
||||
* **cli:** preserve output and comparison input validation ([#981](https://github.com/morluto/rea/issues/981)) ([d5c43d6](https://github.com/morluto/rea/commit/d5c43d65f0f6a1de8fb2eb5f2c1940a6ea2af20c))
|
||||
* **cli:** preserve unsupported historical-source imports ([#795](https://github.com/morluto/rea/issues/795)) ([911d580](https://github.com/morluto/rea/commit/911d58063477912042da5e9e573a9e8008956622))
|
||||
* **contracts:** align CLI/MCP boundary contracts ([#946](https://github.com/morluto/rea/issues/946)) ([4a60738](https://github.com/morluto/rea/commit/4a6073810a955d058c10f209157cbd68832a92c5))
|
||||
* **deps:** remediate smol-toml advisories ([93091ab](https://github.com/morluto/rea/commit/93091abb4dbd3cceee217d179db816895df24d70))
|
||||
* **deps:** remediate smol-toml advisories ([c55ae3b](https://github.com/morluto/rea/commit/c55ae3b9eccc17e6e8f4369e13044995d0650704))
|
||||
* **dotnet:** compare members with undecoded signatures by exact identity ([28fd49f](https://github.com/morluto/rea/commit/28fd49f0f6cf54e373411f5999e6b5722dd826f5))
|
||||
* **dotnet:** compare members with undecoded signatures by exact identity ([d52af3b](https://github.com/morluto/rea/commit/d52af3b321a321964598fb302edeb26552c9dffd))
|
||||
* **dotnet:** inspect native boundaries of non-managed and malformed PEs ([d5f28fb](https://github.com/morluto/rea/commit/d5f28fb29c5c3e8acef940fcae042e660e3a4d72))
|
||||
* **dotnet:** inspect native boundaries of non-managed and malformed PEs ([d98dcfa](https://github.com/morluto/rea/commit/d98dcfa8c2e46a7cc918aeb01058c26c4a84060c))
|
||||
* **dotnet:** keep a decoded member unknown beside an undecoded counterpart ([76b7020](https://github.com/morluto/rea/commit/76b70203d941ab85c1772f330407fd832d438c9c))
|
||||
* **dotnet:** keep admitted CLI header facts when metadata is unreadable ([e5ba693](https://github.com/morluto/rea/commit/e5ba6934e4ff819c09077ca70bd2417d37f85bc0))
|
||||
* **dotnet:** name the shared key in exact-signature ambiguities ([7714e76](https://github.com/morluto/rea/commit/7714e763d9ac2e87994068b3dee37ca6c5ffc6f4))
|
||||
* **dotnet:** treat ambiguous undecoded members as possible counterparts ([73370e1](https://github.com/morluto/rea/commit/73370e1c064899753988581c9a821e0e7f579561))
|
||||
* **errors:** report union input issues at the union's path ([bddeb63](https://github.com/morluto/rea/commit/bddeb634b583cf4f7d5fa6cf3a1ee821e05d2923))
|
||||
* **evaluation:** canonicalize Unicode argument keys ([#980](https://github.com/morluto/rea/issues/980)) ([0a4caf4](https://github.com/morluto/rea/commit/0a4caf4a0a1355b6a121fc88886b22e55843ac76))
|
||||
* **firmware:** accept the verified Binwalk and Unblob release lines ([#844](https://github.com/morluto/rea/issues/844)) ([bef4321](https://github.com/morluto/rea/commit/bef4321afb3d0d480e8a04ad7e256663542caa3d))
|
||||
* **firmware:** retain multi-file extraction failures ([#978](https://github.com/morluto/rea/issues/978)) ([8aaf9a1](https://github.com/morluto/rea/commit/8aaf9a19788beb6612332505850ad3407cb00027))
|
||||
* **ghidra:** admit the 12.1 line and the install's JDK range ([#840](https://github.com/morluto/rea/issues/840)) ([5cbf06c](https://github.com/morluto/rea/commit/5cbf06c7ad0ace599721bffd7da566b8a790b622))
|
||||
* **ghidra:** select exact packaged bridge scripts ([#800](https://github.com/morluto/rea/issues/800)) ([894c30e](https://github.com/morluto/rea/commit/894c30ef02d946f0c8de3ae6c9168d9525d95033))
|
||||
* **ida:** share concurrent session cleanup ([#950](https://github.com/morluto/rea/issues/950)) ([f14b6a8](https://github.com/morluto/rea/commit/f14b6a84306116716e78c5ffc84353ac07939593))
|
||||
* **installer:** validate exact semantic versions ([#954](https://github.com/morluto/rea/issues/954)) ([248972f](https://github.com/morluto/rea/commit/248972f327776687037dc07099117584c0674e77))
|
||||
* integrate 24 reviewed browser/native/semantics corrections ([#815](https://github.com/morluto/rea/issues/815)) ([3af3811](https://github.com/morluto/rea/commit/3af3811ab0a212b6aab6185628d47ecad37958fc))
|
||||
* **javascript:** advertise the exact empty seed rule and match empty API keys ([35e5508](https://github.com/morluto/rea/commit/35e5508fd095a062d7477bd6bb7d2d8e28201cd8))
|
||||
* **javascript:** analyze source maps with empty source names ([58ef8ac](https://github.com/morluto/rea/commit/58ef8ac3aa803d7b64dd12a5b2a2e8ec24261060))
|
||||
* **javascript:** analyze sources containing empty string literals ([e7cbfe0](https://github.com/morluto/rea/commit/e7cbfe04fdfd7affe50dba9e2c7a9a0a72291041))
|
||||
* **javascript:** analyze sources containing empty string literals ([11fc3b9](https://github.com/morluto/rea/commit/11fc3b981509e8107b10b2e75363f5bb489879db))
|
||||
* **javascript:** filter keyed-collection receivers only for get/delete ([00f707b](https://github.com/morluto/rea/commit/00f707b250451246d69a1205c39c6843fab6bb5b))
|
||||
* **javascript:** handle deep AST traversal and addition without stack overflow ([#915](https://github.com/morluto/rea/issues/915)) ([62b3711](https://github.com/morluto/rea/commit/62b37111e7f09887eb712c6e8e721e0334cf10b9))
|
||||
* **javascript:** handle deep static member paths ([#939](https://github.com/morluto/rea/issues/939)) ([348c65c](https://github.com/morluto/rea/commit/348c65c7ece1aee280f9431d995996b444e7549d))
|
||||
* **javascript:** keep bare relative URLs for computed-method XHR ([e32028d](https://github.com/morluto/rea/commit/e32028d1b566872ba8cd08fc6800c198d6aef30b))
|
||||
* **javascript:** keep computed-method XHR request URLs ([602d002](https://github.com/morluto/rea/commit/602d0021d45a6cdab9f806554f3ee12ea670ec97))
|
||||
* **javascript:** keep exact empty env keys and method names ([522c2d0](https://github.com/morluto/rea/commit/522c2d0ecb1eb7be262eaef2926cceac9a68badc))
|
||||
* **javascript:** keep exact empty env keys and method names ([f7954a8](https://github.com/morluto/rea/commit/f7954a8fd692f262ef3ce1507b957681150ce9c4)), closes [#783](https://github.com/morluto/rea/issues/783)
|
||||
* **javascript:** keep exact empty require paths and request fields ([ad1f6b5](https://github.com/morluto/rea/commit/ad1f6b508fbdd44d8bf1884ed96ae69082808e7d)), closes [#783](https://github.com/morluto/rea/issues/783)
|
||||
* **javascript:** keep exact empty values in graph identities ([f608127](https://github.com/morluto/rea/commit/f608127635ed8e0d105ad95252e5ea0dff899f5b))
|
||||
* **javascript:** keep exact empty values in graph identities ([63012ee](https://github.com/morluto/rea/commit/63012eeb601fc74ca82f3a027d852a5da1b099d2))
|
||||
* **javascript:** keep keyed-collection reads out of endpoints ([9075ce7](https://github.com/morluto/rea/commit/9075ce71ada4334407aa2d194dc82e3031e30260))
|
||||
* **javascript:** keep keyed-collection reads out of endpoints ([eb68761](https://github.com/morluto/rea/commit/eb68761eb0a35b876e33dd6115d0d07f97e99dd5))
|
||||
* **javascript:** keep literal HTTP methods on storage-named receivers ([9f48a76](https://github.com/morluto/rea/commit/9f48a76e1186cc43c6928eff1c82e93280caee1a))
|
||||
* **javascript:** keep literal-method and URL-literal requests on aliased receivers ([e3e7664](https://github.com/morluto/rea/commit/e3e76646657e950e24241ba715eb97ea385933e9))
|
||||
* **javascript:** keep mutated object initializer values unknown ([#752](https://github.com/morluto/rea/issues/752)) ([6caa38a](https://github.com/morluto/rea/commit/6caa38a0cad369ddff52b08f8d7d44c1b92ecac0))
|
||||
* **javascript:** keep storage open versions out of XHR endpoints ([4f51103](https://github.com/morluto/rea/commit/4f5110306d9edab94bf63bf81cf259324d96af57))
|
||||
* **javascript:** keep storage open versions out of XHR endpoints ([898b0f6](https://github.com/morluto/rea/commit/898b0f62d76ea1cf793140cb255cad16ab85b44d))
|
||||
* **javascript:** keep XHR and client endpoints that receiver spellings hid ([05f43ed](https://github.com/morluto/rea/commit/05f43ed388ae4e9316d320b592b6eba5b119ce89))
|
||||
* **javascript:** leave empty application values unlabeled ([527b76e](https://github.com/morluto/rea/commit/527b76ef78e84d29bb41bd45f1f83fae05ca66ca))
|
||||
* **javascript:** match exact empty literal channels and seeds ([e20da1f](https://github.com/morluto/rea/commit/e20da1f205b105b1b6a5decd3c2d0fe36fd12410))
|
||||
* **javascript:** never read window or document open as XHR ([ae849a7](https://github.com/morluto/rea/commit/ae849a72811857ba9755962ec48a1d76c9d1181a))
|
||||
* **javascript:** preserve computed Electron member uncertainty ([3a4a63f](https://github.com/morluto/rea/commit/3a4a63f70a2e42fb1fa48881e08b08d612289f2e))
|
||||
* **javascript:** preserve computed Electron member uncertainty ([1260cac](https://github.com/morluto/rea/commit/1260cacc67b8e0c93fb9434452053feca956e7e7))
|
||||
* **javascript:** project decoded file URL paths ([#810](https://github.com/morluto/rea/issues/810)) ([2572e1e](https://github.com/morluto/rea/commit/2572e1eb2f853a4cebfc8303d9e293e1d106d608))
|
||||
* **javascript:** read open() endpoints only from XHR method/URL pairs ([63e0822](https://github.com/morluto/rea/commit/63e0822cc794bd6ec1688340b3230ffb6b5c4a8c))
|
||||
* **javascript:** read open() endpoints only from XHR method/URL pairs ([1be3705](https://github.com/morluto/rea/commit/1be370543f7ea39016f86ebdec80a8ceaf7f8a28))
|
||||
* **javascript:** read substitution-free templates as exact strings ([c371d6e](https://github.com/morluto/rea/commit/c371d6e1e9cceb28e6cfc874e14c4e740bb87751))
|
||||
* **javascript:** read substitution-free templates as exact strings ([77cf757](https://github.com/morluto/rea/commit/77cf75710369acc6c304ec352ab25aa7274e04b1))
|
||||
* **javascript:** resolve bare source-map and worker URLs next to the script ([#890](https://github.com/morluto/rea/issues/890)) ([45fcd28](https://github.com/morluto/rea/commit/45fcd28b4018723bdb508a9229b74ab9eb749f75))
|
||||
* **javascript:** retain recovered call argument positions ([#929](https://github.com/morluto/rea/issues/929)) ([e6ba9c3](https://github.com/morluto/rea/commit/e6ba9c3af3053485f154825eea6a156127328be9))
|
||||
* **linux:** improve Hopper reliability and CachyOS support ([#963](https://github.com/morluto/rea/issues/963)) ([98b2b02](https://github.com/morluto/rea/commit/98b2b027afd8c80b68e9daaf97bdaa4d942105cc))
|
||||
* **mcp:** advertise reserved process environment key ([#949](https://github.com/morluto/rea/issues/949)) ([e0d76ae](https://github.com/morluto/rea/commit/e0d76ae80ddc70ae4d5afcbd1427afb81eee3669))
|
||||
* **mcp:** isolate optional observation adapter failures ([#838](https://github.com/morluto/rea/issues/838)) ([be3cfbf](https://github.com/morluto/rea/commit/be3cfbffdfbbb65da75dcec0518e2ebf387e66bc))
|
||||
* **mcp:** preserve exact guided prompt context ([#761](https://github.com/morluto/rea/issues/761)) ([66c26f2](https://github.com/morluto/rea/commit/66c26f280b1d494806ee182b52564166760f0ae2))
|
||||
* **native-ui:** handle incomplete accessibility child collections ([#944](https://github.com/morluto/rea/issues/944)) ([ac3bae3](https://github.com/morluto/rea/commit/ac3bae3cb97c4fb622eb1b6ac1f3b84e6a70ba03))
|
||||
* **native:** inspect plists containing data, dates, and non-finite reals ([bc2cd8b](https://github.com/morluto/rea/commit/bc2cd8b874e115eee446860043758a80bd583ad0))
|
||||
* **native:** inspect plists containing data, dates, and non-finite reals ([5ead2cf](https://github.com/morluto/rea/commit/5ead2cfbc47896b43502363d3568330f5d94762e))
|
||||
* **native:** keep echoed codesign paths out of signature fields ([#865](https://github.com/morluto/rea/issues/865)) ([4aa2b60](https://github.com/morluto/rea/commit/4aa2b60c1d84607a138395ae93260b2e50c2a721))
|
||||
* **native:** keep echoed Mach-O paths out of load commands and symbols ([ab985c6](https://github.com/morluto/rea/commit/ab985c6a10e9da856ed151100cee8459793c3573))
|
||||
* **native:** keep echoed Mach-O paths out of load commands and symbols ([37fe105](https://github.com/morluto/rea/commit/37fe105ba168488d05226f2912bed802e2d48d75))
|
||||
* **native:** keep integral plist reals beyond the JSON range as reals ([#860](https://github.com/morluto/rea/issues/860)) ([3b92a2b](https://github.com/morluto/rea/commit/3b92a2bceddaa1659654da08160813a3affbc6b7))
|
||||
* **native:** keep plist XML element types for large numbers ([f85056d](https://github.com/morluto/rea/commit/f85056d83e42a461c8c947792774580294d0ceb9))
|
||||
* **native:** keep the resolved bundle plist and permission failures ([418a556](https://github.com/morluto/rea/commit/418a5562f6f640503c751924a4787586c2c5c98a))
|
||||
* **native:** pass Swift symbols to swift-demangle as operands ([dd90965](https://github.com/morluto/rea/commit/dd90965058a9e6f7dd7f30eb7e462572e777db7f))
|
||||
* **native:** pass Swift symbols to swift-demangle as operands ([6c88094](https://github.com/morluto/rea/commit/6c88094da45258b346fe594764e561f999622a58))
|
||||
* **native:** preserve incomplete native UI captures ([#991](https://github.com/morluto/rea/issues/991)) ([9f496ef](https://github.com/morluto/rea/commit/9f496ef2d4a3aa68816287bf5d2ba1620d72c97a))
|
||||
* **native:** report exact plist integers beyond the JSON number range ([c9f49db](https://github.com/morluto/rea/commit/c9f49db907db0513ad8b22c9b2bbdcee400aff11))
|
||||
* **native:** report exact plist integers beyond the JSON number range ([430c297](https://github.com/morluto/rea/commit/430c297ca4ddb3491c2b835b889486e6a2ffdc17))
|
||||
* **native:** report plist permission denials as access failures ([5738a0c](https://github.com/morluto/rea/commit/5738a0cf8c111833847fecb2f7d0c2ee35eabbd6))
|
||||
* **native:** report why a selected plist cannot be inspected ([14f9b3d](https://github.com/morluto/rea/commit/14f9b3d93119a60a3cf5f3d9c138ca829a5462e8))
|
||||
* **native:** report why a selected plist cannot be inspected ([0674fac](https://github.com/morluto/rea/commit/0674facd468c3321584ece908031feee7220b9c6))
|
||||
* **plist:** keep character references beyond Unicode literal ([5afd8a8](https://github.com/morluto/rea/commit/5afd8a850b2ebd62e3400a5e2ece1a1cfa98bab6))
|
||||
* **plist:** read XML plists containing a __proto__ key ([faaee69](https://github.com/morluto/rea/commit/faaee693d5bb4bb957387ebd0f4338513c4c914a))
|
||||
* **plist:** read XML plists containing a __proto__ key ([a08be51](https://github.com/morluto/rea/commit/a08be51e9f0d23bd46b66bbdb88dbda44bc54c88))
|
||||
* **plist:** report omitted __proto__ entries in signatures and Interface Builder ([15ab5db](https://github.com/morluto/rea/commit/15ab5dbcaf3fac09756e98926373965247dad622))
|
||||
* **plist:** report omitted __proto__ entries in signatures and Interface Builder ([6729ee7](https://github.com/morluto/rea/commit/6729ee7636889a0a2ebc9fca8e35860476f8ca34))
|
||||
* preserve native UI failure reasons and dedupe version literals ([#854](https://github.com/morluto/rea/issues/854)) ([2d4a910](https://github.com/morluto/rea/commit/2d4a910fcd30e8d5b96fcb957b931e9aa3e4ab0c))
|
||||
* **process:** clarify Windows capture support and recovery ([#798](https://github.com/morluto/rea/issues/798)) ([50787b2](https://github.com/morluto/rea/commit/50787b2faf1a4bea799d041960ca2b91199ef403))
|
||||
* **process:** harden filesystem snapshot identity and cancellation ([#956](https://github.com/morluto/rea/issues/956)) ([93aa9eb](https://github.com/morluto/rea/commit/93aa9eb5c2551984182a4025d836e4223e8ecc8a))
|
||||
* **process:** retain provider output until streams close ([#928](https://github.com/morluto/rea/issues/928)) ([b929251](https://github.com/morluto/rea/commit/b9292516f4eedc2dedba713abf57f665ef1fb596))
|
||||
* **reference:** retain partial imports for unreadable directories ([#766](https://github.com/morluto/rea/issues/766)) ([cffc548](https://github.com/morluto/rea/commit/cffc548cd3127c5db811f20db4816039846b3170))
|
||||
* **release:** drive checkpoint releases from frozen branches ([fc58fa1](https://github.com/morluto/rea/commit/fc58fa1eb01fe5a8200a78e9551799c9c1a3d256))
|
||||
* **release:** publish from explicit source checkpoints ([49887f0](https://github.com/morluto/rea/commit/49887f0239da4483153a5e1610058619855525db))
|
||||
* **semantics:** isolate named function expression bindings ([#776](https://github.com/morluto/rea/issues/776)) ([ae81f64](https://github.com/morluto/rea/commit/ae81f64d2ad10e5cdbf8356819351c02480aee7a))
|
||||
* **semantics:** preserve direct promise ownership boundaries ([#758](https://github.com/morluto/rea/issues/758)) ([e1ca412](https://github.com/morluto/rea/commit/e1ca412757bb01be855a66bf72006e3becd497b6))
|
||||
* **semantics:** preserve empty string property keys ([#764](https://github.com/morluto/rea/issues/764)) ([630bdaf](https://github.com/morluto/rea/commit/630bdafcacc621105f6a43ed68a6e51651aca629))
|
||||
* **semantics:** recognize Node builtin default namespaces ([#769](https://github.com/morluto/rea/issues/769)) ([24a0bf9](https://github.com/morluto/rea/commit/24a0bf9a618604e2fa30817d673ce911c7842819))
|
||||
* **semantics:** retain trailing rest argument flows ([#756](https://github.com/morluto/rea/issues/756)) ([4bfd803](https://github.com/morluto/rea/commit/4bfd8038be0a60c43a452bec15e23799a0b040c0))
|
||||
* **setup:** accept the pinned Hopper RPM launcher ([#933](https://github.com/morluto/rea/issues/933)) ([dda8e6a](https://github.com/morluto/rea/commit/dda8e6a72890f87bdf8bbd3250b68b6648aae553))
|
||||
* **setup:** read an OpenCode V2 server named type as a table entry ([#866](https://github.com/morluto/rea/issues/866)) ([4e17be5](https://github.com/morluto/rea/commit/4e17be57053fb0ce2561d92e05b6e38ef789b2d4))
|
||||
* **setup:** register OpenCode V2 configurations in mcp.servers ([957069d](https://github.com/morluto/rea/commit/957069d14d832ddeac3f90dd141d379b81e2832a))
|
||||
* **setup:** register OpenCode V2 configurations in mcp.servers ([69d8afe](https://github.com/morluto/rea/commit/69d8afefc19e014a4bba7a182983a9e77e100a75))
|
||||
* **target:** decode app Info.plist executables with an XML parser ([c57f1b7](https://github.com/morluto/rea/commit/c57f1b74e0af5cf5fbb459cb247e1448cbf8df1b))
|
||||
* **target:** decode app Info.plist executables with an XML parser ([c182f72](https://github.com/morluto/rea/commit/c182f72ad3d9eea10b084a43527acb7c8ca2d6bf))
|
||||
* **test:** disconnect Hopper fixture IPC on SIGTERM ([b912b11](https://github.com/morluto/rea/commit/b912b117f019d5d3e4b4e3ef683e9b25f3a709bc))
|
||||
* **windows:** accept ordinary drive path separators ([#791](https://github.com/morluto/rea/issues/791)) ([b62931f](https://github.com/morluto/rea/commit/b62931f8335f58bfe344883a4f149e9eef73d835))
|
||||
* **windows:** remove cancelled snapshot outputs ([#957](https://github.com/morluto/rea/issues/957)) ([d76f7d1](https://github.com/morluto/rea/commit/d76f7d150895289aa1fb85a057b81ca7ff102df4))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **artifacts:** stream ASAR members without full buffering ([#962](https://github.com/morluto/rea/issues/962)) ([abd7a5e](https://github.com/morluto/rea/commit/abd7a5ed091061e4b1e83e5933e9009fb5b8d577))
|
||||
* **call-path:** prune dead ends and enumerate without recursion ([#909](https://github.com/morluto/rea/issues/909)) ([e52fe97](https://github.com/morluto/rea/commit/e52fe9719fec03f2aa79b1a1b9e11b38c229903c))
|
||||
* **cli:** defer catalog digests and use canonical cache scope ([9a15525](https://github.com/morluto/rea/commit/9a1552529889e9f5b52a88739eb767b92e57e24e))
|
||||
* **cli:** defer unused catalog work during command startup ([#920](https://github.com/morluto/rea/issues/920)) ([9a15525](https://github.com/morluto/rea/commit/9a1552529889e9f5b52a88739eb767b92e57e24e))
|
||||
* **comparison:** reuse canonical sort keys ([#990](https://github.com/morluto/rea/issues/990)) ([ebb065e](https://github.com/morluto/rea/commit/ebb065e2dc15f5763276f401521896f66bae6339))
|
||||
* **dotnet:** index declaring-type ranges once per metadata table ([#918](https://github.com/morluto/rea/issues/918)) ([7a9f587](https://github.com/morluto/rea/commit/7a9f587c3a9998d828b83388438976527ca7074f))
|
||||
* **dotnet:** index ordered declaring-type ranges once per table ([7a9f587](https://github.com/morluto/rea/commit/7a9f587c3a9998d828b83388438976527ca7074f))
|
||||
* **evidence:** scale imports and dependency graphs ([#941](https://github.com/morluto/rea/issues/941)) ([7dc40d3](https://github.com/morluto/rea/commit/7dc40d3d9e00caf013109942a5d9a1d7fdf9ec62))
|
||||
* **inspector:** bound runtime script location authorization ([#945](https://github.com/morluto/rea/issues/945)) ([58381ee](https://github.com/morluto/rea/commit/58381eefa24f73580e31eaa09dc14b9c04d78d6c))
|
||||
* **javascript:** analyze member chains without recursive copying ([#932](https://github.com/morluto/rea/issues/932)) ([dcde958](https://github.com/morluto/rea/commit/dcde9584508830a5d99ac5e2032f539563c69002))
|
||||
* **javascript:** index Promise return-site ownership ([#971](https://github.com/morluto/rea/issues/971)) ([2cedde9](https://github.com/morluto/rea/commit/2cedde9f1cf72b04bef37f9f004bb68d5ff6502a))
|
||||
* **javascript:** index semantic fingerprint relations ([#942](https://github.com/morluto/rea/issues/942)) ([c92a344](https://github.com/morluto/rea/commit/c92a344d4770125d85a11f3fc7a23d800bd30003))
|
||||
* **javascript:** reduce analysis and source-map work ([#955](https://github.com/morluto/rea/issues/955)) ([c7530f9](https://github.com/morluto/rea/commit/c7530f9a816d84354516d3ea137f4a0d91d3b8b3))
|
||||
* **providers:** assemble fragmented responses once per line ([#911](https://github.com/morluto/rea/issues/911)) ([df7151b](https://github.com/morluto/rea/commit/df7151ba305eab3a33a9261a023705b29862b308))
|
||||
* **reconstruction:** index coverage evaluation ([#983](https://github.com/morluto/rea/issues/983)) ([f26c8b5](https://github.com/morluto/rea/commit/f26c8b5b8cff10b93173359b5453874d659f2440))
|
||||
* **setup:** reuse initial diagnostics during planning ([#940](https://github.com/morluto/rea/issues/940)) ([118d43e](https://github.com/morluto/rea/commit/118d43e787e38857714852cc846b1b604a61950d))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* add incremental capability migration safeguards ([#819](https://github.com/morluto/rea/issues/819)) ([75f50d8](https://github.com/morluto/rea/commit/75f50d86f6d504f4dd97a707871d39ec022b3047))
|
||||
* **android:** group layered source and verification ([#873](https://github.com/morluto/rea/issues/873)) ([8e8921d](https://github.com/morluto/rea/commit/8e8921d40c63b40ab7e1598f672063dd304d93f0))
|
||||
* **apple:** group artifact producers and verification ([#952](https://github.com/morluto/rea/issues/952)) ([ef1a0cc](https://github.com/morluto/rea/commit/ef1a0cc65af7918ef7204a39f42a4299b486c453))
|
||||
* **artifacts:** isolate JavaScript file acquisition ([#916](https://github.com/morluto/rea/issues/916)) ([f0a68dc](https://github.com/morluto/rea/commit/f0a68dce60e81b4d2f420bf774b61957a37326ac))
|
||||
* **binary:** group session selection and snapshot ownership ([#886](https://github.com/morluto/rea/issues/886)) ([dd114d3](https://github.com/morluto/rea/commit/dd114d39801973e958a582c8fcba7ccce0e50299))
|
||||
* **binary:** move production composition outside application workflows ([#965](https://github.com/morluto/rea/issues/965)) ([33d4761](https://github.com/morluto/rea/commit/33d4761cc7a96630b7c2fed890df83d433c45914))
|
||||
* **browser:** align inspector exclusions with canonical vocabulary ([#834](https://github.com/morluto/rea/issues/834)) ([de9593a](https://github.com/morluto/rea/commit/de9593ad48c0b97507f18949b7ad86ad9c743ebc))
|
||||
* **cli:** split core analysis command registrar ([#858](https://github.com/morluto/rea/issues/858)) ([8b9940a](https://github.com/morluto/rea/commit/8b9940a7b5d3fcd5a8eda6d397f0ab7c3aaf2ac9))
|
||||
* colocate investigation record implementation ([#867](https://github.com/morluto/rea/issues/867)) ([bb19c94](https://github.com/morluto/rea/commit/bb19c9477ef3eefd9c5b2e6cabe78cc94e6779ae))
|
||||
* compose investigation record ownership ([#845](https://github.com/morluto/rea/issues/845)) ([ce73c3f](https://github.com/morluto/rea/commit/ce73c3fed2e5fd20ae0113579345a69763eb7655))
|
||||
* declare auxiliary capabilities from source ([#853](https://github.com/morluto/rea/issues/853)) ([4a6c17c](https://github.com/morluto/rea/commit/4a6c17ce83dfd20412f0e168525314a20c61169e))
|
||||
* **domain:** consolidate property-key extraction behind semanticStaticPropertyKey ([#823](https://github.com/morluto/rea/issues/823)) ([764ff05](https://github.com/morluto/rea/commit/764ff05231f1a666dfa655492da42b4b6ba742c3))
|
||||
* **domain:** converge comparison mechanics on shared vocabulary ([#824](https://github.com/morluto/rea/issues/824)) ([c13c9d4](https://github.com/morluto/rea/commit/c13c9d41f8441a711266adee21429fb7433a6aa1))
|
||||
* **domain:** extract artifact path syntax helpers ([#830](https://github.com/morluto/rea/issues/830)) ([ef535b8](https://github.com/morluto/rea/commit/ef535b81fefc358b0bdb655e5aa057fc3949ce5f))
|
||||
* **domain:** extract source-map envelope walk and disambiguate stringValue ([#828](https://github.com/morluto/rea/issues/828)) ([64ebdf4](https://github.com/morluto/rea/commit/64ebdf45016bda679674cbd2287576eadf66a1fc))
|
||||
* **domain:** map validation taxonomies and add inventory Result seam ([#833](https://github.com/morluto/rea/issues/833)) ([78f4848](https://github.com/morluto/rea/commit/78f4848d70ce462426e9f221d6e7b3e34219bf46))
|
||||
* **domain:** split Interface Builder keyed-archive parse from graph builder ([#859](https://github.com/morluto/rea/issues/859)) ([da09706](https://github.com/morluto/rea/commit/da0970672da725dbb095ffcaefad07303ae85aab))
|
||||
* **firmware:** group layered source and verification ([#878](https://github.com/morluto/rea/issues/878)) ([507e8b3](https://github.com/morluto/rea/commit/507e8b3c7be75f8de69be214618394aca3585404))
|
||||
* **ghidra:** extract createClient and load-image attestation ([#856](https://github.com/morluto/rea/issues/856)) ([b7df5b7](https://github.com/morluto/rea/commit/b7df5b78052539e57967db8e6562befd70c6ebdc))
|
||||
* give Inspector its own capability directory ([#869](https://github.com/morluto/rea/issues/869)) ([021ba69](https://github.com/morluto/rea/commit/021ba690b07eab4b990be47f90946db9f70e0fc0))
|
||||
* **javascript:** group application workflows and verification ([#896](https://github.com/morluto/rea/issues/896)) ([0804722](https://github.com/morluto/rea/commit/0804722cdf721a8d767d02f9342ebdf8d186f7e1))
|
||||
* **javascript:** group domain semantics and contracts ([#907](https://github.com/morluto/rea/issues/907)) ([f6a58f1](https://github.com/morluto/rea/commit/f6a58f10629614fb7a6970421218b73d41bce6a5))
|
||||
* **managed:** group capability workflows and verification ([#926](https://github.com/morluto/rea/issues/926)) ([28713f9](https://github.com/morluto/rea/commit/28713f9b4dcb882f0e811653890fde70cb5fdadb))
|
||||
* **native:** group analyst semantics workflows and contracts ([#973](https://github.com/morluto/rea/issues/973)) ([ea4db4c](https://github.com/morluto/rea/commit/ea4db4c9f7cbc10dc701c4d5770109c08952a5b8))
|
||||
* **native:** split Apple dispatch metadata decode facets ([#855](https://github.com/morluto/rea/issues/855)) ([676959e](https://github.com/morluto/rea/commit/676959ecb55b606a567af3d2eb3d3edc96f0aa2f))
|
||||
* **provider:** declare deep capabilities from source ([#892](https://github.com/morluto/rea/issues/892)) ([b808e2d](https://github.com/morluto/rea/commit/b808e2d6bbbae38c1ff2e293e1cf77464d889bdd))
|
||||
* share Android and firmware provider factories ([#826](https://github.com/morluto/rea/issues/826)) ([3ed59b2](https://github.com/morluto/rea/commit/3ed59b281247f2180b3c79e658078507e4cc1be2))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add concise lawful-use disclaimer ([#888](https://github.com/morluto/rea/issues/888)) ([4037c65](https://github.com/morluto/rea/commit/4037c6545dc8cc47f4602d8f06e5329ed641cdbb))
|
||||
* add DX-Ball reconstruction showcase ([#876](https://github.com/morluto/rea/issues/876)) ([895079d](https://github.com/morluto/rea/commit/895079dd093f02487789d9aa967f0cccbfe379d8))
|
||||
* add prominent website and showcase links to READMEs ([2ccc86c](https://github.com/morluto/rea/commit/2ccc86cd13d71f08b553d3b9962175144b882539))
|
||||
* add README star history chart ([#850](https://github.com/morluto/rea/issues/850)) ([11f27de](https://github.com/morluto/rea/commit/11f27deddf88703368942d3ddfc239a7ebe72e3e))
|
||||
* add website and showcase links to READMEs ([#930](https://github.com/morluto/rea/issues/930)) ([2ccc86c](https://github.com/morluto/rea/commit/2ccc86cd13d71f08b553d3b9962175144b882539))
|
||||
* **apple:** describe directory-based framework plist ambiguity ([6d74992](https://github.com/morluto/rea/commit/6d74992efed3271e8370c505afdde347609f8353))
|
||||
* audit and improve localized readmes translations and wording ([00a6ac3](https://github.com/morluto/rea/commit/00a6ac3de99f432bd6dbb88148a2aab5f8d3d338))
|
||||
* clarify NativeAOT scope and regenerate conformance ledger ([c945931](https://github.com/morluto/rea/commit/c945931961cbd488163c6d92ef91336ed506942e))
|
||||
* fix stale IDA roadmap item, mermaid paths, toolchain wording ([d2aed17](https://github.com/morluto/rea/commit/d2aed172ab79d98ff4e62c14d8f34cc49c1d17b4))
|
||||
* move quick start up and mark skill install optional ([554e9b1](https://github.com/morluto/rea/commit/554e9b1612f7799c2bc7177eb1a9b04a0f27d2e9))
|
||||
* note Cloud Agent Node.js toolchain ([#837](https://github.com/morluto/rea/issues/837)) ([6aa6350](https://github.com/morluto/rea/commit/6aa6350c800796d0953937e894ff176ae248e930))
|
||||
* publish VitePress documentation on GitHub Pages ([c78fe69](https://github.com/morluto/rea/commit/c78fe6990bcface6853ac5c11c90df6bc0df562d))
|
||||
* separate ambiguous and unavailable provider recovery ([c3f2b8e](https://github.com/morluto/rea/commit/c3f2b8e7742915dc27ab7c801a8a1a9127169896))
|
||||
* show task-scoped readiness and provider preference in the quick start ([880cca1](https://github.com/morluto/rea/commit/880cca1bca497bb53c97a821b470df0f2235e043))
|
||||
* show task-scoped readiness and provider preference in the quick start ([5fd0db3](https://github.com/morluto/rea/commit/5fd0db37d94f1c1a0495920c79e121d799da0d30)), closes [#726](https://github.com/morluto/rea/issues/726)
|
||||
* update release boundary to npm 4.1.0 with Windows native bundle ([69be514](https://github.com/morluto/rea/commit/69be514c67c8b2eb8b450355a9782d9237748fda))
|
||||
* **website:** add Notion case study and improve onboarding ([a6ba772](https://github.com/morluto/rea/commit/a6ba772c08e2648e6602787a0ea537d75d72802d))
|
||||
* **website:** add TH04 bullet-ring case study ([f8d84da](https://github.com/morluto/rea/commit/f8d84da127fad871050c098b004a503b36795ad0))
|
||||
* **website:** add worked guides and keep Pages publishing manual ([#925](https://github.com/morluto/rea/issues/925)) ([4f25ee3](https://github.com/morluto/rea/commit/4f25ee33dcd4195f8d877f8835f81c4e08c95235))
|
||||
* **website:** clarify guides and case studies ([5065fcd](https://github.com/morluto/rea/commit/5065fcd5205ec7c0d259131431c2cf54d9f69cf6))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **artifacts:** report compiled connector records when a storyboard route is missing ([d421d3f](https://github.com/morluto/rea/commit/d421d3f286ce515b5126859a0f9cac60667a168e))
|
||||
* **artifacts:** report observed action routes when a compiled route is missing ([c60b737](https://github.com/morluto/rea/commit/c60b737f7db8e2c9a1982cdb4357fcda1f47ff08))
|
||||
* **artifacts:** write the storyboard fixture's action in Cocoa syntax ([53bfa7e](https://github.com/morluto/rea/commit/53bfa7e3f792cdb9f2e95fc954f3616426414bcc))
|
||||
* eliminate fixture races and prune redundant scaffolding ([#849](https://github.com/morluto/rea/issues/849)) ([f72931d](https://github.com/morluto/rea/commit/f72931d9dd54609bfca422750fc2667f80af691c))
|
||||
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
* consolidate macOS native baseline checks ([#935](https://github.com/morluto/rea/issues/935)) ([810fe53](https://github.com/morluto/rea/commit/810fe53e799b89dc215c52c9245d36fb2a44d9c4))
|
||||
* verify native arm64 and Intel macOS workflows ([#898](https://github.com/morluto/rea/issues/898)) ([4801a24](https://github.com/morluto/rea/commit/4801a24089964860db7f4855be21d2d4313c7eab))
|
||||
|
||||
## [4.1.0](https://github.com/morluto/rea/compare/rea-agents-4.0.1...rea-agents-4.1.0) (2026-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **android:** integrate static APK analysis with headless JADX ([#738](https://github.com/morluto/rea/issues/738)) ([904d03e](https://github.com/morluto/rea/commit/904d03e987a6c498561b314a266202dee84b13ab))
|
||||
* **evidence:** accept exact retained application references ([#734](https://github.com/morluto/rea/issues/734)) ([d0f3480](https://github.com/morluto/rea/commit/d0f3480a3cb9887499c2b188c783dd1c4db09787))
|
||||
* **firmware:** integrate Binwalk and Unblob analysis ([5a7c769](https://github.com/morluto/rea/commit/5a7c769692c21efc4cc0e5ebf06a9f2534dc75ed))
|
||||
* **ghidra:** add atomic function annotation edits ([#709](https://github.com/morluto/rea/issues/709)) ([9bab553](https://github.com/morluto/rea/commit/9bab55337ab10616be6929afddb08fb19fbb8ab7))
|
||||
* **ghidra:** admit explicit DOS COM analysis ([#702](https://github.com/morluto/rea/issues/702)) ([5bcb664](https://github.com/morluto/rea/commit/5bcb6645b561e666b7e07dd8f38d2296fa7cbba8))
|
||||
* **ghidra:** enable Windows x64 read-only analysis ([#699](https://github.com/morluto/rea/issues/699)) ([26881da](https://github.com/morluto/rea/commit/26881da76aa320c8529fcc741df0fc9898d97bdc))
|
||||
* **ghidra:** integrate optional NativeAOT metadata recovery ([#748](https://github.com/morluto/rea/issues/748)) ([fb679b6](https://github.com/morluto/rea/commit/fb679b63c27e6c5f137532953cfa24a2ffb1aaf2))
|
||||
* **ghidra:** verify DOS load images and expose memory evidence ([#697](https://github.com/morluto/rea/issues/697)) ([a6a8f67](https://github.com/morluto/rea/commit/a6a8f67649cab558b0de11601e65425f280c7170))
|
||||
* **ida:** add read-only GUI and headless MCP providers ([#754](https://github.com/morluto/rea/issues/754)) ([8238232](https://github.com/morluto/rea/commit/82382324855a3a4ed51d2cdc3b2b455c9e8419e2))
|
||||
* **setup:** support Command Code MCP registration ([#645](https://github.com/morluto/rea/issues/645)) ([7f1ae42](https://github.com/morluto/rea/commit/7f1ae42f71ce85547d317eed5e62fa330d9876bb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **android:** validate explicit Java runtime ([#742](https://github.com/morluto/rea/issues/742)) ([7390390](https://github.com/morluto/rea/commit/739039092f98c67e8e6ccfb1f8445e8f13ac8f15))
|
||||
* **artifacts:** make extraction output tree work on Windows ([#753](https://github.com/morluto/rea/issues/753)) ([c8e66c7](https://github.com/morluto/rea/commit/c8e66c704a70f5133922aa1bd1a3ba42959093ba))
|
||||
* **browser:** classify CSP host sources by their own origin ([#707](https://github.com/morluto/rea/issues/707)) ([1d15bcb](https://github.com/morluto/rea/commit/1d15bcb9ecd7efa487a8fd3f0d9755bd60051722))
|
||||
* **browser:** keep bare module locations unresolved ([#676](https://github.com/morluto/rea/issues/676)) ([8189081](https://github.com/morluto/rea/commit/8189081511441f0e3909b21b80c21c658ec9a453))
|
||||
* **ci:** upload Vitest blob reports from current path ([77799e6](https://github.com/morluto/rea/commit/77799e6c90ce36cd52e45b62c9cba2d46641c4cf))
|
||||
* **cli:** disable unmanaged skill synchronization ([433ebda](https://github.com/morluto/rea/commit/433ebda8b492f388faf6e6cac63ccc6f9c2d8d7e))
|
||||
* **cli:** disable unmanaged skill synchronization ([2c1bf92](https://github.com/morluto/rea/commit/2c1bf9201040ed3d60f475af9129b7bf85a508fa))
|
||||
* **cli:** fail when JSON input files cannot be parsed ([18c1829](https://github.com/morluto/rea/commit/18c18290d048b78a435f6864dfc778a9fea0af2e))
|
||||
* **cli:** fail when JSON input files cannot be parsed ([3201f05](https://github.com/morluto/rea/commit/3201f0538dc88e54bce00decf689bf9b5603bfd3))
|
||||
* **cli:** include build step in missing-runtime recovery ([6a37346](https://github.com/morluto/rea/commit/6a37346acb9829acaab0e2042624e6ea0d03e2a9))
|
||||
* **cli:** include build step in missing-runtime recovery ([ae72abd](https://github.com/morluto/rea/commit/ae72abd019b25347bfc7593b5dcadc5a2cbc3786))
|
||||
* **cli:** report reference-source import failures ([00eb03d](https://github.com/morluto/rea/commit/00eb03d0f2247da95dfa7bda0f41a0f366a25dd9))
|
||||
* **cli:** report reference-source import failures ([431e8eb](https://github.com/morluto/rea/commit/431e8ebfcfba2925904c0deb4888bbd3755b17bf))
|
||||
* **comparison:** break function collection collation ties ([c3d013b](https://github.com/morluto/rea/commit/c3d013b2fd5c3a32dfa73ef40dafdaeaf935f37e))
|
||||
* **comparison:** break function collection collation ties ([3aaf7e2](https://github.com/morluto/rea/commit/3aaf7e2c54029a456145d6e3287fd9122f3abbe3))
|
||||
* **comparison:** break JSON shape collation ties ([217e8cf](https://github.com/morluto/rea/commit/217e8cf59f49e2f68b394fbcbc87f914dbee97ee))
|
||||
* **comparison:** break JSON shape collation ties ([805c338](https://github.com/morluto/rea/commit/805c338f360184d73ae335a723fca656830d6328))
|
||||
* **comparison:** retain container-only export shape paths ([a55b5bb](https://github.com/morluto/rea/commit/a55b5bb8ee400ef5565954d57916e8aae7dac760))
|
||||
* **comparison:** retain container-only export shape paths ([7864d66](https://github.com/morluto/rea/commit/7864d665e15edec29ade0f3fb7ac374b8579c2c0))
|
||||
* **dotnet:** preserve leading Unicode content in metadata strings ([#667](https://github.com/morluto/rea/issues/667)) ([ddea063](https://github.com/morluto/rea/commit/ddea06360a09508a0ff695ab6e141eff19534619))
|
||||
* **electron:** respect computed option keys and overrides ([af48fe8](https://github.com/morluto/rea/commit/af48fe879161ee749692751b3e29ba06b63b7409))
|
||||
* **electron:** respect computed option keys and overrides ([98de338](https://github.com/morluto/rea/commit/98de3380e3062cb25778263bfea07b215a09b39c))
|
||||
* **evidence:** admit current process provider identity ([ab5a1c6](https://github.com/morluto/rea/commit/ab5a1c61b12475c554a5e4d60b48b642b2464bbb))
|
||||
* **evidence:** admit current process provider identity ([d311940](https://github.com/morluto/rea/commit/d311940f2414fe5cceb750de4464b681bb44ae1e))
|
||||
* **firmware:** validate configured executables ([#751](https://github.com/morluto/rea/issues/751)) ([9a4e6da](https://github.com/morluto/rea/commit/9a4e6da5a96ed2708a0f628b436dcd7415999327))
|
||||
* **ghidra:** preserve request and shutdown failure diagnostics ([#647](https://github.com/morluto/rea/issues/647)) ([19f8cdd](https://github.com/morluto/rea/commit/19f8cddfd6f283188567b8ff1e49b810d79952ee))
|
||||
* **ghidra:** refuse ambiguous universal Mach-O imports ([b1b687f](https://github.com/morluto/rea/commit/b1b687fb75325c0275a0cf161843fc5969ba1d47))
|
||||
* **ghidra:** refuse ambiguous universal Mach-O imports ([6582b24](https://github.com/morluto/rea/commit/6582b2433a955167e33246ad07de1a40bcb20144))
|
||||
* **hopper:** retain shutdown request diagnostics ([8d7071d](https://github.com/morluto/rea/commit/8d7071da4c61c5cfb4894be6536405affd90f276))
|
||||
* **hopper:** retain shutdown request diagnostics ([7254871](https://github.com/morluto/rea/commit/72548712fea972b920834663a5e2ceea4738ce1e))
|
||||
* integrate 14 approved fix PRs (668,671,673,675,678-681,685-690) ([#705](https://github.com/morluto/rea/issues/705)) ([cf06115](https://github.com/morluto/rea/commit/cf061159ab69a746675e48896fa953d6990c05f9))
|
||||
* **javascript:** count all source line terminators ([e60aa11](https://github.com/morluto/rea/commit/e60aa1110800e1309b482e7a44ecd1762bade992))
|
||||
* **javascript:** count all source line terminators ([03bc6d3](https://github.com/morluto/rea/commit/03bc6d36db94e70c709557cb7302d40af9e99e6b))
|
||||
* **javascript:** expose result schema failures ([6d1f39b](https://github.com/morluto/rea/commit/6d1f39b250f8a1c6f924b1a6b9d26d56dff4beee))
|
||||
* **javascript:** expose result schema failures ([99263a7](https://github.com/morluto/rea/commit/99263a720d7106aff059ad7fa43849151a94c963))
|
||||
* **javascript:** hash graph and evidence JSON incrementally ([#646](https://github.com/morluto/rea/issues/646)) ([9f42495](https://github.com/morluto/rea/commit/9f42495d11100adc8646de5c8b62762ab9715ae6))
|
||||
* **javascript:** preserve path.resolve reset semantics ([863ed47](https://github.com/morluto/rea/commit/863ed47de2e35ef9b734e45e3bb44ea9e9dc8ff6))
|
||||
* **javascript:** preserve path.resolve reset semantics ([4177377](https://github.com/morluto/rea/commit/417737748aa96ba38ced7b0b22dbb972187f2ee0))
|
||||
* **javascript:** preserve primitive addition coercion ([#666](https://github.com/morluto/rea/issues/666)) ([94cc847](https://github.com/morluto/rea/commit/94cc8475e0e9e24cd2e8345bd36a743a979e3620))
|
||||
* **javascript:** preserve unknown branches in primitive unions ([75a75cb](https://github.com/morluto/rea/commit/75a75cbb20aa9b2148f9fd0547f613eadda68edd))
|
||||
* **javascript:** preserve unknown branches in primitive unions ([9a2f395](https://github.com/morluto/rea/commit/9a2f3950286a01922be0b07b29c4aefee466ec1e))
|
||||
* **javascript:** read source-map directives only from comments ([#672](https://github.com/morluto/rea/issues/672)) ([7f1fa64](https://github.com/morluto/rea/commit/7f1fa64019e01a654ad0eb80b47d268a58f04e57))
|
||||
* **javascript:** scope candidate ambiguity to query traversal ([6ad62ce](https://github.com/morluto/rea/commit/6ad62cec6abf182a52cf9844145e147fb6399c8e))
|
||||
* **javascript:** scope candidate ambiguity to query traversal ([f94fc79](https://github.com/morluto/rea/commit/f94fc7967b2e5604c144d3638515caad6273a0c6))
|
||||
* **mcp:** retain shutdown failure diagnostics ([09c249d](https://github.com/morluto/rea/commit/09c249deb86068503611fc474e76018d5c2a4d0b))
|
||||
* **mcp:** retain shutdown failure diagnostics ([fd008cd](https://github.com/morluto/rea/commit/fd008cd8e0b8277e691efd8e69dc96ac8e188d9e))
|
||||
* **native:** inspect mixed-signature Mach-O slices ([1dccd0d](https://github.com/morluto/rea/commit/1dccd0dee04abfdc80e8b48e3f05fa39cccf0feb))
|
||||
* **native:** inspect mixed-signature Mach-O slices ([66f3cfa](https://github.com/morluto/rea/commit/66f3cfa594a86cbf1f21fc6d2eca1a82e236c896))
|
||||
* **native:** retain spaced symbols in dyld inventory ([#669](https://github.com/morluto/rea/issues/669)) ([fa02df9](https://github.com/morluto/rea/commit/fa02df9ad3bc3139ef10422dedffac88c4d51d36))
|
||||
* **native:** select concrete arm64e Mach-O slices ([f04445c](https://github.com/morluto/rea/commit/f04445c57c597a8312211139b75da706cb0c2a96))
|
||||
* **native:** select concrete arm64e Mach-O slices ([fd3282a](https://github.com/morluto/rea/commit/fd3282a87e40417cf3d7d6211743809f9bad2dc8))
|
||||
* **process:** honor disabled PID normalization in samples ([44b3627](https://github.com/morluto/rea/commit/44b3627ab0b32c42e545dd2fd095006d249a77f6))
|
||||
* **process:** honor disabled PID normalization in samples ([a4e5736](https://github.com/morluto/rea/commit/a4e57366dd75077bde07f25c7966204cedad929e))
|
||||
* **process:** scope comparison unknowns to their evidence ([#732](https://github.com/morluto/rea/issues/732)) ([9dd6cef](https://github.com/morluto/rea/commit/9dd6cef90abf89f9191bce942e31900870231968))
|
||||
* **reconstruction:** accept function comparison source parameters ([c3bc38b](https://github.com/morluto/rea/commit/c3bc38bf6c74d2175a4680530dcadd6cab0e7791))
|
||||
* **reconstruction:** accept function comparison source parameters ([7a96391](https://github.com/morluto/rea/commit/7a96391001780012f704f4ef5cb6597baa590b7d))
|
||||
* **reference:** bound Dockerfile language detection ([132a3a4](https://github.com/morluto/rea/commit/132a3a46be648b036ed36372a50d6e32a9e7f2e5))
|
||||
* **reference:** bound Dockerfile language detection ([53fa559](https://github.com/morluto/rea/commit/53fa55937f241fc9ef3091eb22314cc370c749b9))
|
||||
* **reference:** classify test suffixes before extensions ([e828acc](https://github.com/morluto/rea/commit/e828acce0e0bf3808773590cf2dbb0bc8227df7b))
|
||||
* **reference:** classify test suffixes before extensions ([8b228ae](https://github.com/morluto/rea/commit/8b228ae7010144b69a243c9682aeb0a1dc4249a3))
|
||||
* **reference:** distinguish computed require member names ([ba35b55](https://github.com/morluto/rea/commit/ba35b5509db60c18dd43e350e0dd4a0b5f229c69))
|
||||
* **reference:** distinguish computed require member names ([df6e476](https://github.com/morluto/rea/commit/df6e476d67b2719c4b998daa3e9ad8a4b157ace2))
|
||||
* **reference:** preserve unresolved absolute module specifiers ([#670](https://github.com/morluto/rea/issues/670)) ([c8aed47](https://github.com/morluto/rea/commit/c8aed4740d52b1eecaa93b3a527e8b3814dc88a4))
|
||||
* **reference:** read linked worktree commit provenance ([#677](https://github.com/morluto/rea/issues/677)) ([d565ecd](https://github.com/morluto/rea/commit/d565ecdc257514f5d0c5a412081e322fc66d4215))
|
||||
* **reference:** recognize CMake build manifests ([#665](https://github.com/morluto/rea/issues/665)) ([1a24be5](https://github.com/morluto/rea/commit/1a24be574d1860931ff8d4c8ec07f3968fff9fd5))
|
||||
* **reference:** retain dynamic import relationships ([a5d2986](https://github.com/morluto/rea/commit/a5d2986b1e517b71a1ed51be433c95e975eafe32))
|
||||
* **reference:** retain dynamic import relationships ([b89de8f](https://github.com/morluto/rea/commit/b89de8feab389477a468f0b89b8dfa35d7ddc102))
|
||||
* **reference:** retain recovered parser diagnostics ([11d0ef2](https://github.com/morluto/rea/commit/11d0ef2e09308ac06889865b3fcf356b8ffa647f))
|
||||
* **reference:** retain recovered parser diagnostics ([ed3a7f0](https://github.com/morluto/rea/commit/ed3a7f070cf0793e22b9a0bbfd1f2ebe9dabcfd0))
|
||||
* **setup:** accept leading BOM in JSON client configs ([a5b970c](https://github.com/morluto/rea/commit/a5b970c6bf6848145a82de51c001410aa4d50f6f))
|
||||
* **setup:** accept leading BOM in JSON client configs ([fe843ec](https://github.com/morluto/rea/commit/fe843ec0b101544e22d7599d93b36377d3e3bc34))
|
||||
* **setup:** align Node runtime checks with package engines ([5802ef8](https://github.com/morluto/rea/commit/5802ef8746412649bb641332f2fa7e69bb1a86a3))
|
||||
* **setup:** align Node runtime checks with package engines ([ea0b733](https://github.com/morluto/rea/commit/ea0b733687582d6cc28b42beaf2fc7a48be07b6a))
|
||||
* **target:** explain the JavaScript directory analysis route ([#733](https://github.com/morluto/rea/issues/733)) ([8d44b3e](https://github.com/morluto/rea/commit/8d44b3edd36b528c308e32328552ca386dc43e91))
|
||||
* **trace:** match typed seeds against member arrays ([65c8797](https://github.com/morluto/rea/commit/65c879757ea09fabc5fc036d6e3395fd2939995c))
|
||||
* **trace:** match typed seeds against member arrays ([38ebd54](https://github.com/morluto/rea/commit/38ebd54776d661ced2e6cc2c5e4b260653a086d9))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **app:** split Setup and ProcessOwnership modules ([#628](https://github.com/morluto/rea/issues/628)) ([3c84080](https://github.com/morluto/rea/commit/3c84080aab5e5aa6c9fc14bafa07c635b537b64d))
|
||||
* **cli:** collapse duplicate CLI layer into src/cli ([#630](https://github.com/morluto/rea/issues/630)) ([494d4db](https://github.com/morluto/rea/commit/494d4db6d9d519f9258a25504cf29b07d61fbb9f))
|
||||
* **contracts:** split tool contracts by family ([#634](https://github.com/morluto/rea/issues/634)) ([5295d56](https://github.com/morluto/rea/commit/5295d56b105074b290e0d881df43cd13e34a2f8b))
|
||||
* **domain:** consolidate canonical ordering and digest helpers ([#624](https://github.com/morluto/rea/issues/624)) ([ba3869c](https://github.com/morluto/rea/commit/ba3869c597133b63fdcc0077c558923ae2e17a3f))
|
||||
* **domain:** split error taxonomy into thematic modules ([#636](https://github.com/morluto/rea/issues/636)) ([735c100](https://github.com/morluto/rea/commit/735c10059ca161967d71ccf8fa3f62011d2a22ca))
|
||||
* **native:** extract FAT header format switch in Mach-O slice selection ([#632](https://github.com/morluto/rea/issues/632)) ([38b2933](https://github.com/morluto/rea/commit/38b293329ca06c424a58bb59a28728f19015ce22))
|
||||
* **runtime:** centralize safe JSON parsing and preserve error causes ([#629](https://github.com/morluto/rea/issues/629)) ([e7f54b3](https://github.com/morluto/rea/commit/e7f54b3310b7f45af11c1754e468d4d3804191db))
|
||||
* **runtime:** name all silent catch causes without changing diagnostics ([#637](https://github.com/morluto/rea/issues/637)) ([5f27e02](https://github.com/morluto/rea/commit/5f27e02fae670774667de3618c88ba2ab0e7b661))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add the official Trendshift badge to README headers ([b6c21b1](https://github.com/morluto/rea/commit/b6c21b1db5d4ff4be0968aa26693916537622a06))
|
||||
* add the official Trendshift badge to README headers ([7d4ed59](https://github.com/morluto/rea/commit/7d4ed59816388409885310f40c12dfdcdd2d9246))
|
||||
* align MCP discovery and skill guidance with the stable catalog ([#664](https://github.com/morluto/rea/issues/664)) ([39db9fe](https://github.com/morluto/rea/commit/39db9fe4245cc8142f57f4feeadaff64cb894e31))
|
||||
* **browser:** add an interaction example that captures the visible result ([#750](https://github.com/morluto/rea/issues/750)) ([91daaa6](https://github.com/morluto/rea/commit/91daaa68aea3bbee02b837ac64d3ad13127d601e))
|
||||
* clarify onboarding and align provider guidance ([#745](https://github.com/morluto/rea/issues/745)) ([5af1699](https://github.com/morluto/rea/commit/5af169910391f6b95492865247fabf3c4735a9d6))
|
||||
* correct CLI examples and documentation checks ([#655](https://github.com/morluto/rea/issues/655)) ([fc80ea8](https://github.com/morluto/rea/commit/fc80ea88dfb7bde43e89db0f341c99d7b53c1a7c))
|
||||
* fix architecture diagram and pretty-print tool catalog ([#626](https://github.com/morluto/rea/issues/626)) ([5e7f35c](https://github.com/morluto/rea/commit/5e7f35c7c3ea8d504421a2623eafd1d73fea08a0))
|
||||
* **ghidra:** explain first-query deadlines and recovery ([#736](https://github.com/morluto/rea/issues/736)) ([e2c23f3](https://github.com/morluto/rea/commit/e2c23f3640f83c13587603c763dbd7b5ef1bbbb0))
|
||||
* **hopper:** document HopperTargetLease realpath fallback ([#711](https://github.com/morluto/rea/issues/711)) ([d3f498c](https://github.com/morluto/rea/commit/d3f498ceec65e4f61f39036ab943ace7ef1b9de5))
|
||||
* **installation:** pin manual MCP registration to the release version ([#658](https://github.com/morluto/rea/issues/658)) ([8dd1d84](https://github.com/morluto/rea/commit/8dd1d8447146b9962ec7ab2f245b0fe47d3ddf44))
|
||||
* update Trendshift repository badge ([3f6d6dc](https://github.com/morluto/rea/commit/3f6d6dc756d99ca6e7cdc1737522842fcbaee839))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* extract shared fixtures, drop knip carve-outs ([#627](https://github.com/morluto/rea/issues/627)) ([55e5eb0](https://github.com/morluto/rea/commit/55e5eb090bb3d6cf347d7cc2f8f028b35b7461ba))
|
||||
* **hopper:** synchronize startup cancellation with launcher readiness ([#662](https://github.com/morluto/rea/issues/662)) ([88a3c3f](https://github.com/morluto/rea/commit/88a3c3f8a4bf5cb31f60bca4696431c4e4512b30))
|
||||
|
||||
## [4.0.1](https://github.com/morluto/rea/compare/rea-agents-4.0.0...rea-agents-4.0.1) (2026-10-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **release:** allow npm propagation time ([2ef02ce](https://github.com/morluto/rea/commit/2ef02ced1603809f0fc6984612de31dce1801dd8))
|
||||
|
||||
## [4.0.0](https://github.com/morluto/rea/compare/rea-agents-3.2.1...rea-agents-4.0.0) (2026-10-05)
|
||||
|
||||
|
||||
|
||||
+101
-37
@@ -2,11 +2,38 @@
|
||||
|
||||
REA welcomes focused bug fixes, documentation improvements, tests, and reverse-engineering workflow enhancements. Open an issue before a large contract or architecture change so its scope can be agreed before implementation.
|
||||
|
||||
## Choosing a contribution
|
||||
|
||||
Small contributions are welcome when they solve a concrete problem, including
|
||||
broken commands, misleading documentation, and missing regression coverage.
|
||||
Explain the problem, keep the diff focused, and share how you checked the change.
|
||||
|
||||
Please avoid standalone cosmetic changes without a clear benefit, such as fixing
|
||||
a typo in an internal comment, changing capitalization or punctuation in
|
||||
already-clear prose, or reformatting unchanged code. Include related polish in a
|
||||
useful fix when practical. For cosmetic-only suggestions, please open an issue
|
||||
instead of a standalone PR so they can be collected and addressed together.
|
||||
Corrections to confusing explanations or incorrect instructions are welcome.
|
||||
|
||||
Avoid speculative cleanup without a concrete problem to solve. Keep related
|
||||
corrections in one PR.
|
||||
|
||||
Before requesting review, read the final diff and run the relevant checks. Be clear
|
||||
about anything you couldn't test. You should be able to explain the changes you
|
||||
submit.
|
||||
|
||||
We may decline changes when the benefit doesn't justify the review or maintenance
|
||||
work.
|
||||
|
||||
For capability organization and provider composition, follow the incremental
|
||||
[migration guide](docs/capability-migration.md). Run `npm run verify:test-discovery`
|
||||
after adding or moving tests.
|
||||
|
||||
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
|
||||
|
||||
## Development setup
|
||||
|
||||
REA development requires Node.js 24.18.x and npm 11.16.x. Real-Hopper verification additionally requires either macOS 12+ or an officially supported Linux host (Ubuntu 24.04+, Fedora 41+, or 64-bit Arch) and an installed Hopper application. Linux demo verification uses its own private Xvfb display and does not require a desktop session. Run `nvm use` before installing dependencies.
|
||||
REA development requires Node.js 24.18.x and npm 11.16.x (pinned toolchain via `nvm use`; the supported runtime range is Node.js ^22.19 || ^24.11 || >=26, as the README badge states). Real-Hopper verification additionally requires either macOS 12+ or an officially supported Linux host (Ubuntu 24.04+, Fedora 41+, 64-bit Arch, or CachyOS) and an installed Hopper application. Linux demo verification uses its own private Xvfb display and does not require a desktop session. Run `nvm use` before installing dependencies.
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
@@ -15,12 +42,35 @@ npm run check:fast
|
||||
|
||||
`npm ci` installs the exact dependencies and prepares the Husky hooks without
|
||||
building the project. Run `npm run build:cached` when you need the standalone
|
||||
CLI or MCP server; `npm run build` remains the uncached compiler leaf used by
|
||||
Turbo. Turbo caches deterministic builds and static checks across Git
|
||||
worktrees. After a package, lockfile, or managed-skill version change, run
|
||||
CLI or MCP server; it compiles the runtime and bundles the authored skill.
|
||||
`npm run build` forces those tasks to rerun. Neither command generates test
|
||||
catalogs, documentation projections, or managed verification evidence. Turbo
|
||||
caches deterministic builds and static checks across Git worktrees. After a package, lockfile, or managed-skill version change, run
|
||||
`npm run metadata:generate` before building.
|
||||
|
||||
Keep dependencies flowing inward through the existing domain, contracts, provider, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the canonical tool inventory defined by `TOOL_CONTRACTS` unless a deliberate contract change updates every verifier, generated catalog artifact, and snapshot. Prefer capability- and session-scoped tool advertisement over schema truncation.
|
||||
Keep dependencies flowing inward through the existing domain, contracts, provider, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the canonical tool inventory defined by `TOOL_CONTRACTS` unless a deliberate contract change updates every verifier, generated catalog artifact, and snapshot. Keep tool discovery complete and report capability- and session-scoped availability through `binary_session`.
|
||||
|
||||
## Documentation website
|
||||
|
||||
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
|
||||
for live editing, `npm run docs:build` to check the production build and
|
||||
links, and `npm run docs:preview` to preview that build at `/rea/`.
|
||||
|
||||
Site navigation lives in `docs/.vitepress/config.ts`. Keep links to guides
|
||||
relative so they work on GitHub and the website; link to repository files
|
||||
outside `docs/` using their full GitHub URLs. Generated reference documents
|
||||
use `npm run docs:generate`; `docs:build` generates them before building the website.
|
||||
|
||||
Pull requests run `npm run docs:check`. `.github/workflows/pages.yml` is a
|
||||
manual VitePress build and does not publish. The public site at
|
||||
<https://rea.tools/> serves `website/public/` through Cloudflare Workers. See
|
||||
the [website README](website/README.md#cloudflare-workers) for previews and
|
||||
manual deployments.
|
||||
|
||||
The separate GitHub Pages host uses only the manual
|
||||
`.github/workflows/website-pages.yml` workflow on `main`. The repository's
|
||||
**Settings → Pages → Build and deployment → Source** must be set to
|
||||
**GitHub Actions** before deploying there.
|
||||
|
||||
## Development feedback and PR verification
|
||||
|
||||
@@ -47,14 +97,46 @@ Packaging, setup, installation, or distribution changes also require
|
||||
`npm run verify:package` and `npm pack --dry-run`. Provider behavior changes
|
||||
require the matching real-provider `verify:*` lane.
|
||||
|
||||
`check:fast` runs cached typecheck and lint, reporting diagnostics on failure.
|
||||
`check:fast` runs cached source/test typecheck and lint without compiling or
|
||||
running generators. `npm run test:prepare` prepares all artifacts consumed by
|
||||
the complete suite; focused tests prepare only their runtime and declared
|
||||
artifact dependencies. The test-only MCP catalog lives at
|
||||
`.cache/mcp-tool-catalog.json` and can be regenerated independently with
|
||||
`npm run mcp-catalog:generate`.
|
||||
|
||||
Build and generation commands hold one SQLite exclusive transaction around
|
||||
their whole Turbo graph, including cache restoration. The persistent database
|
||||
in `.cache/rea-command-locks/` is a lock identity, not a stale-file marker;
|
||||
the operating system releases ownership when the command supervisor exits.
|
||||
On POSIX, that supervisor retains the transaction and process-group identity
|
||||
through cancellation, including loss of its parent runner. Windows cancellation
|
||||
waits for the command to complete. Do not remove the database while commands
|
||||
are running.
|
||||
|
||||
`check` adds formatting, dead-code, and package-metadata freshness checks.
|
||||
Formatting uses Oxfmt and the committed `.oxfmtrc.json`; generated sources use
|
||||
the same configuration. Pre-commit formats and lints staged files; pre-push runs
|
||||
`check:fast`.
|
||||
`docs:check` checks committed generated metadata without rendering API HTML.
|
||||
`docs:generate` regenerates those files; render API HTML separately with
|
||||
`npm run docs:api:cached`. PR CI renders and uploads the `api-docs` artifact.
|
||||
`docs:check` builds and validates generated metadata for the current checkout.
|
||||
The product catalog (`docs/public/product-catalog.json`), portable managed
|
||||
conformance projections (`docs/verification/managed-conformance-*.json`), and
|
||||
packaged skill (`skills/`) are ignored generated outputs. `docs:generate`
|
||||
prepares documentation and managed evidence; `evidence:generate` prepares only
|
||||
the managed commitment and its runtime/skill dependencies. Edit skill
|
||||
instructions and references in `skill-src/`; the skill task adds metadata to
|
||||
the packaged copy without rewriting authored files. The generated manifest
|
||||
commits to that exact packaged skill bundle. It is a portable projection of
|
||||
the deterministic managed verifier, not a record of optional real-provider runs.
|
||||
CI validates these outputs and retains them as artifacts instead of pushing
|
||||
generated-only commits onto feature branches. Reviewed source metadata such as
|
||||
`src/generatedPackageMetadata.ts` and `docs/error-contract.schema.json` remains
|
||||
tracked and checked for freshness. Do not commit ignored generated outputs.
|
||||
The build-generated product catalog contains documented facts and their provider
|
||||
identity, rather than full runtime schema hashes. The managed skill contains
|
||||
instructions and inventory metadata; doctor compares its installed files with
|
||||
the canonical bundle. Schema-only fixes should not change these outputs or the
|
||||
skill commitment in the conformance manifest. Runtime schema identity remains
|
||||
available through doctor and `binary_session`.
|
||||
Real-provider execution remains uncached; deterministic builds use Turbo.
|
||||
|
||||
Local `npm test` runs every deterministic Vitest project without coverage or
|
||||
@@ -101,32 +183,14 @@ Describe the behavior change and verification performed in the pull request. Nev
|
||||
|
||||
## Maintainer release checklist
|
||||
|
||||
Run `npm run check:pr`, `npm run docs:api:cached`, the isolated package verifier, package dry run, and two-target real-Hopper verifier described above. Build a local tarball and exercise the executable through the package boundary:
|
||||
Use the [release guide](docs/releasing.md). Main pushes automatically refresh a
|
||||
Release Please PR with the next version and changelog. Review its migration
|
||||
notes, wait for the current CI checks to pass, and merge it into main to publish.
|
||||
The Release workflow validates and tags that merge; both publishers build its
|
||||
exact SHA. Ordinary main pushes cannot publish packages. An optional manual
|
||||
`release/VERSION` checkpoint retains explicit preparation and publication.
|
||||
|
||||
```bash
|
||||
npm pack
|
||||
```
|
||||
|
||||
Use the exact filename printed by `npm pack` to run the packaged executable:
|
||||
|
||||
```bash
|
||||
npm exec --yes --package ./rea-agents-VERSION.tgz -- rea --help
|
||||
```
|
||||
|
||||
Replace `VERSION` with the packed version; do not use a tarball from an earlier
|
||||
build.
|
||||
|
||||
Publish the public package:
|
||||
|
||||
```bash
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
After npm registry propagation, verify the published CLI and connect the client SDK version pinned in `package.json` to the published server to confirm the canonical tool catalog:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest --help
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest setup --yes --all-detected
|
||||
npx -y rea-agents@latest mcp
|
||||
```
|
||||
Keep new implementation commits on main for the next release. The workflow
|
||||
owns packaged-artifact verification, npm publication, the published CLI/MCP
|
||||
canary, and MCP Registry publication. Sync manual checkpoint metadata back to
|
||||
main after publication; see the guide for recovery and verification.
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
<div align="center">
|
||||
|
||||
**English** · [简体中文](README_zh.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [العربية](README_ar.md)
|
||||
**English** · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
### Reverse engineer anything with agents, from app behavior down to native binaries.
|
||||
### One MCP for reverse engineering across binaries, applications, and runtime behavior.
|
||||
|
||||
**See a feature you like. Understand how it works, down to the binary level.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#tool-catalog-for-investigation)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[Quick start](#quick-start) · [Current status](#current-status) · [Investigation model](#the-investigation-model) · [Tool catalog](#tool-catalog-for-investigation) · [Roadmap](#roadmap) · [How it works](#how-it-works)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
|
||||
|
||||
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [FAQ](#faq) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
@@ -47,619 +52,290 @@ See a feature in an app that you want in your own product? Ask your agent to inv
|
||||
|
||||
REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.
|
||||
|
||||
Setup configures your agent and connects it to Hopper or Ghidra. If you need an analysis tool, setup can install Hopper for you.
|
||||
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper or Ghidra installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs neither engine.
|
||||
|
||||
## Just ask your agent
|
||||
> **[Visit the REA website](https://rea.tools/)** for setup instructions, illustrated guides, and real case studies.
|
||||
|
||||
After [setup](#quick-start), restart your agent and ask:
|
||||
## Quick start
|
||||
|
||||
### Set up your agent
|
||||
|
||||
With Node.js and npm installed, run:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Choose your agents, review the proposed changes, and approve them. Setup adds
|
||||
REA's MCP server and matching workflow instructions, with backups of existing
|
||||
configuration. Restart your agent afterward.
|
||||
|
||||
Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
|
||||
[other agents](docs/installation.md#supported-agents). See
|
||||
[installation and setup](docs/installation.md) for provider configuration and
|
||||
manual MCP registration.
|
||||
|
||||
### Ask your agent
|
||||
|
||||
```text
|
||||
Understand how search works in the Notes app, show me the evidence, and build a
|
||||
similar feature for my project.
|
||||
```
|
||||
|
||||
Replace Notes with the app you want to understand, or ask for an overview first.
|
||||
Replace Notes with your target app and the feature you want to understand.
|
||||
|
||||
## The investigation model
|
||||
### Use the terminal
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Decompile</strong><br /><br />
|
||||
Open an app and recover readable code, strings, names, and other clues about how it works.
|
||||
</td>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Understand</strong><br /><br />
|
||||
Follow the code from one part of the app to another until the agent can explain how a feature actually works.
|
||||
</td>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Recreate</strong><br /><br />
|
||||
Turn what the agent learned into a feature for your own product, adapted to your stack, interface, and requirements.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
REA shows how it reached its conclusions. It does not claim to recover original source code or automatically clone an application.
|
||||
|
||||
## Why REA
|
||||
|
||||
| | |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| **Built for agents** | Ask what an app does and let your agent inspect it instead of guessing. |
|
||||
| **CLI and MCP** | Run the same reverse-engineering capabilities from your terminal or agent. |
|
||||
| **Guided setup** | Configure your agent, connect an existing analysis tool, or install Hopper with your approval. |
|
||||
| **From insight to code** | Understand a feature, then build your own version in the same coding session. |
|
||||
| **Local by design** | Analysis runs on your supported local host. REA does not upload the app to a hosted analysis service. |
|
||||
| **Keeps context** | Investigate several apps without starting over for every question. |
|
||||
|
||||
## Quick start
|
||||
|
||||
### Run setup (recommended)
|
||||
|
||||
Set up REA with your agent:
|
||||
Inspect an extracted JavaScript/Electron app directory or ASAR:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Choose which supported agents should use REA, then review the exact paths and changes before approving. Existing REA registrations are selected by default; newly detected agents are available to select, but detection alone does not select them. Setup adds MCP access and REA's guided workflow for selected agents. Hopper is a separate optional choice with its own consent. Setup can also record an existing Ghidra installation.
|
||||
The result includes modules, imports, Electron boundaries and their evidence.
|
||||
Replace the path with your target, such as `"D:/apps/example"` on Windows.
|
||||
|
||||
Setup shows its changes before applying them and backs up existing configuration. See [Installation and setup](docs/installation.md) for requirements and setup options.
|
||||
|
||||
### With an agent (recommended)
|
||||
|
||||
After setup, restart your agent and [describe the app or feature](#just-ask-your-agent) you want to understand. Hopper can run in demo mode; if it shows a first-run prompt, choose the demo or enter an existing license.
|
||||
|
||||
REA supports Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, and VS Code. Existing REA registrations are selected by default during setup; other detected agents remain unselected until chosen. Other agents can use the [manual MCP configuration](#manual-mcp-configuration).
|
||||
|
||||
### From the terminal with npx
|
||||
|
||||
After setup, run:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
### Install the rea command
|
||||
|
||||
Install the command-line interface:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
The installer adds `rea` to your system and starts setup when run in a terminal. It requires Node.js and npm to be installed already.
|
||||
|
||||
Alternatively, install with npm, then run setup:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
Update either installation with `rea update`.
|
||||
|
||||
### Requirements
|
||||
|
||||
- macOS 12 or newer
|
||||
- Ubuntu 24.04+, Fedora 41+, or 64-bit Arch Linux
|
||||
- Node.js 22.19+ or 24.11+ (including newer releases)
|
||||
- npm; REA does not require or install a particular npm version
|
||||
|
||||
Native binary analysis requires [Hopper](https://www.hopperapp.com/) or [Ghidra](#ghidra-read-only-analysis-provider). Hopper is separate software with its own license; its demo supports analysis with vendor-defined limits. REA can use Ghidra that you have already installed.
|
||||
|
||||
Windows Ghidra support is experimental and currently unavailable. The required Windows process ownership, private-directory permissions, and safe-path checks are not implemented. See [Windows Ghidra P0](docs/windows-ghidra-p0.md) for the remaining requirements.
|
||||
|
||||
If something is not working, run:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
```
|
||||
|
||||
`doctor` checks your host, dependencies, analysis tools, and agent configuration without changing them. Use `--json` for structured diagnostics.
|
||||
|
||||
### Linux installation and troubleshooting
|
||||
|
||||
On macOS, setup can install Hopper in `~/Applications` after approval. It verifies the official download and does not need Homebrew or administrator privileges.
|
||||
|
||||
On supported Linux distributions, setup can install Hopper and its demo-session dependencies through your system package manager. You may see a system authorization prompt. Demo sessions use a private virtual display, leaving your desktop alone. See [Hopper installation](docs/installation.md#hopper) for download verification and platform details.
|
||||
|
||||
The normal Linux launcher is `/opt/hopper/bin/Hopper`. If Hopper was installed elsewhere:
|
||||
|
||||
```bash
|
||||
export HOPPER_LAUNCHER_PATH=/absolute/path/to/Hopper
|
||||
rea doctor --json
|
||||
```
|
||||
|
||||
If doctor reports a missing analysis engine even though the file exists, inspect shared-library resolution with:
|
||||
|
||||
```bash
|
||||
ldd /opt/hopper/bin/Hopper | grep 'not found'
|
||||
```
|
||||
|
||||
Install the missing packages and rerun `rea setup`. The Linux demo needs Xvfb, Python 3, X11, and XTEST; approved setup installs these dependencies. If you use the curl installer, add `~/.local/bin` to your shell `PATH` when needed.
|
||||
|
||||
REA defaults `HOPPER_LAUNCHER_PATH` to `/Applications/Hopper Disassembler.app/Contents/MacOS/hopper` on macOS and `/opt/hopper/bin/Hopper` on Linux. Explicit configuration always takes precedence.
|
||||
|
||||
### Ghidra read-only analysis provider
|
||||
|
||||
Already use Ghidra? REA can connect it to your agent on Linux x64 or macOS x64/arm64. It requires **Ghidra 12.1.4** and a **64-bit JDK 21**. On macOS, your Ghidra installation must also include the native decompiler for your architecture.
|
||||
|
||||
Set the installation paths, then run setup:
|
||||
|
||||
```bash
|
||||
export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.4_PUBLIC
|
||||
export JAVA_HOME=/absolute/path/to/jdk-21 # optional when java and javac resolve from PATH
|
||||
rea doctor --json
|
||||
rea setup
|
||||
rea providers --json
|
||||
```
|
||||
|
||||
Setup checks the installations and saves their paths in your selected agents' configuration after approval. Ghidra and Java must already be installed; REA does not download or change them.
|
||||
|
||||
The adapter exposes **22 read-only operations** for functions, strings, symbols, assembly, decompilation, calls, references, instructions, and data types. These also support REA's overview, search, call-graph, and function-analysis workflows. Ghidra does not provide GUI controls or annotation changes through REA.
|
||||
|
||||
REA analyzes a temporary copy of the target and removes the temporary project when the session closes. Results identify what Ghidra observed and what it could not resolve. Decompilation produces pseudocode rather than the original source.
|
||||
|
||||
Ghidra also imports DOS MZ executables with an explicit 16-bit x86 real-mode profile. Function results include complete observed body ranges, distinguishing owned bytes from the enclosing span. See the [DOS analysis guide](docs/ghidra-dos.md) for addresses, packing, and verification boundaries.
|
||||
|
||||
Windows operations remain unavailable pending the controls described in the [Windows Ghidra P0 guide](docs/windows-ghidra-p0.md). See [Ghidra installation](docs/installation.md#ghidra), [provider evaluation](docs/provider-evaluation.md), and [testing](docs/testing.md) for configuration details, coverage, and real-provider verification.
|
||||
|
||||
To remove only REA-owned MCP registrations and the managed skill:
|
||||
|
||||
```bash
|
||||
rea uninstall
|
||||
rea uninstall --purge-data # also removes only ~/.rea/cache and ~/.rea/state
|
||||
```
|
||||
|
||||
Uninstall preserves Hopper, Node.js, Evidence files, captures, unrelated skills, and other MCP servers. It refuses malformed client configuration and never follows purge-data symlinks.
|
||||
|
||||
### CLI or agent?
|
||||
|
||||
| If you want to… | Use |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| Ask an agent to investigate an app and build a feature | Run setup, restart your agent, then describe the task |
|
||||
| Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
|
||||
| Validate, canonicalize, or compare Evidence bundles | `rea evidence-import`, `rea evidence-export`, or `rea compare` |
|
||||
| Map a local JavaScript/Electron application without executing it | `rea analyze PATH` or `rea analyze-javascript-application` |
|
||||
| Reuse immutable analysis results without relaunching a provider | Pass `--snapshot /path/to/analysis.json` to a deep-analysis command |
|
||||
| Import source as historical reference | `rea import-reference-source` |
|
||||
| Capture or compare controlled process behavior | `rea capture-process` or `rea compare-process-captures` |
|
||||
|
||||
```bash
|
||||
rea evidence-import /absolute/path/to/evidence/bundle.json
|
||||
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
||||
rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
|
||||
```
|
||||
|
||||
Analyze a JavaScript application directory or ASAR without executing it:
|
||||
|
||||
```bash
|
||||
rea analyze /absolute/path/to/releases/app.asar --json
|
||||
rea analyze-javascript-application /absolute/path/to/releases/app.asar --json
|
||||
```
|
||||
|
||||
For a directory or `.asar`, generic `rea analyze` automatically selects the
|
||||
static JavaScript application provider when neither `--provider` nor
|
||||
`--snapshot` is supplied. Both routes return the analysis and its Evidence
|
||||
context inline.
|
||||
|
||||
Import an older source tree as a reference. REA keeps it separate from observations of the current app:
|
||||
|
||||
```bash
|
||||
rea import-reference-source /absolute/path/to/source
|
||||
```
|
||||
|
||||
Imports read the path supplied to the command. File names do not cause automatic omissions; files are represented by hashes and metadata. To exclude selected paths, set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON string array of ignore patterns. Exports never replace an existing file unless `--overwrite` is explicit.
|
||||
|
||||
Use a snapshot to save successful analysis results and reuse them on later runs. REA reuses a result only when the target bytes, operation, parameters, analysis tool, and settings match. It does not cache changes or cursor-dependent calls. Snapshot files stay local and use owner-only permissions.
|
||||
|
||||
```bash
|
||||
rea analyze /absolute/path/to/app --snapshot /absolute/path/to/analysis/app.json
|
||||
# The same exact query can be answered from that snapshot.
|
||||
rea analyze /absolute/path/to/app --snapshot /absolute/path/to/analysis/app.json
|
||||
```
|
||||
|
||||
Exact CLI cached-evidence reads happen before any provider process starts. In MCP sessions,
|
||||
pass `snapshot_path` to `open_binary` to import a snapshot atomically while
|
||||
opening its matching target; MCP providers may still start before a cached call
|
||||
result is returned. Pass `snapshot_path` and, when required, `overwrite: true` to
|
||||
`close_binary` to save atomically before Hopper resources are released. If the
|
||||
save fails, REA deliberately leaves the session open.
|
||||
|
||||
## One prompt, a full investigation
|
||||
|
||||
```text
|
||||
Reverse engineer the Notes app. Find how offline search works, explain it,
|
||||
and build a version for my project using TypeScript and SQLite.
|
||||
```
|
||||
|
||||
REA gives the agent a clear path from that request to working code:
|
||||
|
||||
| Step | What the agent does | REA tools |
|
||||
| ---: | --------------------------------------- | ---------------------------------------------------------------- |
|
||||
| 1 | Opens and identifies the binary | `open_binary`, `binary_overview` |
|
||||
| 2 | Finds likely offline-search clues | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | Connects those clues to executable code | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | Reconstructs the relevant control flow | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | Decompiles the relevant routines | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | Builds the feature in your project | code adapted to your stack, product, and requirements |
|
||||
|
||||
REA handles the app analysis in steps 1 through 5. The agent performs step 6 with its normal file-editing and test tools, using what it learned about the app.
|
||||
|
||||
## What agents can do
|
||||
|
||||
- Investigate a feature you like and build a version tailored to your own product.
|
||||
- Explain how a feature works when its source code is unavailable.
|
||||
- Reconstruct an app's authentication, storage, update, or networking flow.
|
||||
- Recover enough structure to document an undocumented format or interface.
|
||||
- Trace a suspicious behavior from a string or symbol to the code that implements it.
|
||||
- Turn recovered behavior into product features, tests, migration notes, ports, or interoperable replacements.
|
||||
- Analyze Swift and Objective-C metadata without manually untangling every mangled symbol.
|
||||
- Leave names, comments, and bookmarks in Hopper so human and agent analysis reinforce each other.
|
||||
|
||||
See [native investigation](docs/native-investigation.md) for keyed archives, instruction/call/type primitives, typed dispatch metadata, value traces and native desktop observation.
|
||||
|
||||
## Tool catalog for investigation
|
||||
|
||||
| Tool family | Count | Examples |
|
||||
| ------------------------- | ----: | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native inspection | 39 | functions, pseudocode, assembly, strings, symbols, calls, references, annotations, byte reads, and file offsets |
|
||||
| Investigation workflows | 14 | app overviews, function dossiers, native APIs and dispatch, batch decompilation, feature traces, call paths, call graphs, Swift and Objective-C discovery |
|
||||
| Native macOS utilities | 7 | Mach-O metadata, code signatures, plists, architectures, and Swift demangling without launching Hopper |
|
||||
| Artifact graph | 5 | directory and package inventories, compiled Interface Builder files, Apple asset catalogs, and extraction |
|
||||
| Managed PE/CLI | 7 | .NET identity, metadata, CIL instructions, native dependencies, reconstruction imports, and build comparisons |
|
||||
| Browser observation | 9 | page structure, network metadata, scripts, source maps, WebMCP discovery, screenshots, and capture comparisons |
|
||||
| Electron analysis | 5 | renderer observation, static app mapping, and static/runtime reconciliation |
|
||||
| JavaScript runtime | 2 | Node/Electron Inspector target discovery, script locations, and execution-context events |
|
||||
| Application workflows | 7 | cross-layer feature traces, build comparisons, historical source mapping, static return-shape comparison, and reconstruction checks |
|
||||
| Workspace and observation | 21 | sessions, evidence bundles, navigation context, process/artifact/function comparisons, and open-question tracking |
|
||||
|
||||
The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness records direct behavioral captures.
|
||||
|
||||
## Current status
|
||||
|
||||
REA supports native application, JavaScript, Electron, .NET, and browser investigation on macOS and Linux. Individual tools have platform and runtime prerequisites; use `rea capabilities` to check what is available on your host.
|
||||
|
||||
- **Native binaries:** Open Mach-O, ELF, PE, and Mac `.app` targets through Hopper or Ghidra. Inspect functions, strings, assembly, decompilation, calls, and references. Hopper also accepts `.hop` databases and supports annotations.
|
||||
- **Packages and resources:** Inspect directories, ZIP, APK, IPA, ASAR, plists, compiled Interface Builder files, and Apple asset catalogs. Artifact requests name the input and requested extraction or traversal directly; macOS DMG traversal also requires the host's native mounting support.
|
||||
- **JavaScript and Electron:** Map modules, imports, source maps, routes, IPC channels, storage, and native add-ons without running the app. Compare builds and trace a feature across the recovered graph. Dynamic and ambiguous relationships remain unresolved. See [JavaScript application workflows](docs/javascript-application-workflows.md).
|
||||
- **Websites:** Inspect a selected page in an existing Chrome-family browser. Capture page structure, network metadata, script evidence, and screenshots requested by the call. Passive observation does not navigate or execute page JavaScript. See [browser observation](docs/browser-observation.md).
|
||||
- **Electron and Node runtime observation:** Inspect selected Electron pages or attach to a Node/Electron V8 Inspector target. Inspector observation records script locations and execution-context events; it does not infer imports, IPC activity, or which modules executed. See [runtime observation](docs/javascript-runtime-observation.md).
|
||||
- **.NET assemblies:** Inspect metadata and CIL instructions, compare builds, and check declared native dependencies without loading or running the assembly. Imported decompiler output is labeled as analyst inference. See [managed-code analysis](docs/managed-code-analysis.md).
|
||||
- **Controlled behavior capture:** Run process, browser, or Electron scenarios with the target, actions, and lifecycle declared in each request, then compare the resulting evidence. Missing observations cannot establish that two runs behaved the same way.
|
||||
- **Evidence and comparison:** Save results with artifact identity, provider, locations, confidence, and limitations. Export or import bundles, compare artifacts and functions, and connect static findings to runtime observations without claiming causality from correlation.
|
||||
- **Open questions:** Track unresolved findings, contradictions, and follow-up probes. Reconstruction checks report pass, fail, or unknown rather than treating missing evidence as a pass.
|
||||
- **Guided workflows:** Start six [MCP investigation workflows](docs/mcp-prompts.md) with suggestions based on your current session.
|
||||
|
||||
Windows Ghidra operations are currently unavailable. Hopper-only features, such as GUI controls and annotations, are not available through Ghidra.
|
||||
|
||||
### Website observation with CDP
|
||||
|
||||
REA can inspect an already-running Chrome-family browser through a literal loopback CDP endpoint. Each request names the endpoint and target; an optional origin filter can narrow discovery:
|
||||
|
||||
```bash
|
||||
rea list-browser-targets http://127.0.0.1:9222 --json
|
||||
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID --json
|
||||
```
|
||||
|
||||
The eight passive browser tools work through both CLI and MCP. They inspect the selected page without navigating, clicking, or evaluating its JavaScript. Credentials, cookies, authorization headers, and raw payload values are not retained. A request selects whether to include script sources, accessibility text, screenshots, or console and payload summaries. REA cannot observe activity that happened before it attached. See [browser observation](docs/browser-observation.md) for browser startup, capture options, and limits.
|
||||
|
||||
### Controlled browser scenarios
|
||||
|
||||
`capture_browser_scenario` runs a caller-declared sequence of browser actions through
|
||||
Playwright. Unlike passive observation, it can interact with the page. Each
|
||||
step records evidence such as screenshots, page structure, navigation, and
|
||||
network activity. Missing or truncated observations cannot establish that two
|
||||
runs behaved the same way.
|
||||
|
||||
```bash
|
||||
rea capture-browser-scenario ./scenario.json --json
|
||||
```
|
||||
|
||||
Launch mode owns a temporary browser profile and removes it after terminating
|
||||
the launched browser. Connect mode accepts one exact loopback CDP target and
|
||||
disconnects without closing the external browser. The request supplies the
|
||||
selected executable or endpoint, actions, and any origin or environment
|
||||
selections needed by the scenario. Scenario JSON contains secret references and
|
||||
environment-variable names, never secret values. See the
|
||||
[browser scenario contract](docs/browser-scenario-contract.md).
|
||||
|
||||
### Node and Electron V8 Inspector observation
|
||||
|
||||
Node and Electron runtime observation attaches to an existing Inspector target named in the request:
|
||||
|
||||
```bash
|
||||
rea list-javascript-runtime-targets http://127.0.0.1:9229 --json
|
||||
rea observe-javascript-runtime http://127.0.0.1:9229 TARGET_ID \
|
||||
--runtime-kind node --json
|
||||
```
|
||||
|
||||
REA records script locations and execution-context events without evaluating
|
||||
code or setting breakpoints. These observations do not establish import
|
||||
relationships, event activity, IPC, process identity, or Electron roles. See
|
||||
[Node and Electron runtime observation](docs/javascript-runtime-observation.md)
|
||||
for the exact coverage.
|
||||
|
||||
Exact package, tool-family, provider, setup-client, schema, and CLI facts are generated from source in [`docs/product-catalog.json`](docs/product-catalog.json). PR CI verifies this catalog, narrative documentation, generated schemas, and a clean TypeDoc render.
|
||||
|
||||
## Roadmap
|
||||
|
||||
The [current status](#current-status) section describes shipped capabilities. These are the next areas of work.
|
||||
|
||||
### Now
|
||||
|
||||
1. **Keep documentation accurate:** update the generated catalog and documentation checks when tools, providers, setup options, or versions change.
|
||||
2. **Test more native binaries:** expand architecture and indirect-call coverage across Hopper and Ghidra.
|
||||
|
||||
### Next
|
||||
|
||||
1. **Connect more application layers:** add static extractors and runtime observations to feature traces.
|
||||
2. **Extend .NET analysis:** improve comparisons of obfuscated assemblies and connect managed findings to verified native analysis. See the [managed-code guide](docs/managed-code-analysis.md).
|
||||
3. **Compare more runtime behavior:** expand process, protocol, filesystem, reconnect, and version-comparison coverage.
|
||||
|
||||
### Later
|
||||
|
||||
1. **Expand browser and Electron interaction:** add scenario actions beyond the current click and wait operations.
|
||||
2. **Observe native apps at runtime:** explore LLDB, Frida, system logs, and native API tracing.
|
||||
3. **Evaluate more tools and targets:** assess IDA/Hex-Rays, Binary Ninja, Rizin, LIEF, Windows-native tools, mobile apps, and firmware.
|
||||
|
||||
Setup already lets you choose agent integration and Hopper installation. Support for installing additional analysis tools is future work, described in the [installation roadmap](docs/roadmap.md).
|
||||
|
||||
See [provider evaluation](docs/provider-evaluation.md) for coverage and remaining requirements, and the [native investigation guide](docs/native-investigation.md) for UI, dispatch, and value-flow analysis.
|
||||
|
||||
## Using REA with other agents
|
||||
|
||||
Setup offers supported agent integrations for selection. Existing REA registrations are selected by default; newly detected agents remain unselected until chosen. Any agent that supports local MCP servers can use the configuration below.
|
||||
|
||||
### Manual MCP configuration
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@4.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
Persistent registrations should use one exact package version. `rea setup`
|
||||
maintains that pin, updates the bundled skill at the same time, and gives Codex
|
||||
a 30-second startup allowance for a cold package-runner start. `rea update`
|
||||
installs an exact release and verifies the new executable. It returns an
|
||||
unapplied maintenance plan for existing REA integrations, with a scoped setup
|
||||
command to review and approve their changes. Restart affected agents afterward.
|
||||
|
||||
MCP clients that support prompts can also discover six ordered investigation
|
||||
workflows through `prompts/list`. Their optional identifier arguments use the
|
||||
current session for bounded `completion/complete` suggestions; see
|
||||
[Guided MCP prompts and completion](docs/mcp-prompts.md).
|
||||
|
||||
## How it works
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["Agent"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal --> REA
|
||||
REA --> Session["Target-bound session router"]
|
||||
Session --> Registry["Deep-provider registry<br/>deterministic selection"]
|
||||
Registry --> Hopper["Hopper provider"]
|
||||
Registry --> Ghidra["Ghidra provider<br/>read-only inventory + function analysis"]
|
||||
Hopper --> Runtime["Owned provider runtime<br/>deadline + bounded diagnostics + cleanup"]
|
||||
Ghidra --> Runtime
|
||||
Session --> Native["Native macOS provider"]
|
||||
Session --> Artifact["Artifact graph provider"]
|
||||
REA --> Browser["Browser CDP provider"]
|
||||
REA --> Process["Process capture provider"]
|
||||
Runtime --> Target["Target software"]
|
||||
Process --> Target
|
||||
Native --> Target
|
||||
Artifact --> Target
|
||||
```
|
||||
|
||||
The CLI and MCP server use the same application workflows and evidence contracts. A provider declares which capabilities it supports and the side effects those capabilities may have. Terminal commands are short-lived; an MCP session can retain an active target and evidence ledger for the session.
|
||||
|
||||
## CLI
|
||||
|
||||
The agent workflow above is the easiest way to use REA. For a one-off overview from the Terminal:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest inspect /Applications/Notes.app
|
||||
npx -y rea-agents@latest search /Applications/Notes.app "offline"
|
||||
npx -y rea-agents@latest function /Applications/Notes.app 0x1000
|
||||
npx -y rea-agents@latest xrefs /Applications/Notes.app 0x1000
|
||||
npx -y rea-agents@latest trace /Applications/Notes.app "offline"
|
||||
npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
|
||||
npx -y rea-agents@latest capabilities
|
||||
npx -y rea-agents@latest providers
|
||||
```
|
||||
|
||||
Run `npx -y rea-agents@latest --help` for direct decompilation, bounded search and
|
||||
other options. `analyze` and `inspect` share the same overview workflow;
|
||||
`function`, `xrefs`, and `trace` return the same Evidence envelopes as MCP.
|
||||
|
||||
Or install the `rea` command globally:
|
||||
To install the `rea` command for regular use:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
For native analysis, configure a provider first. See the
|
||||
[CLI and Evidence guide](docs/cli.md) for native commands, provider selection,
|
||||
snapshots and scripting.
|
||||
|
||||
### Update REA
|
||||
|
||||
REA changes quickly, and new releases include frequent bug fixes. Keep your
|
||||
installation up to date.
|
||||
|
||||
For an npm-installed CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA accepts a Mac `.app` folder directly. If an agent cannot find an app by name, tell it where the app is installed.
|
||||
To refresh your agent registrations and skill, run the setup command printed
|
||||
by the update.
|
||||
|
||||
### Choosing a deep-analysis provider
|
||||
|
||||
Choose which analysis tool to use from the CLI:
|
||||
If you use `npx`, update your agent setup with:
|
||||
|
||||
```bash
|
||||
rea providers --json
|
||||
rea analyze /absolute/path/to/program --provider hopper
|
||||
REA_ANALYSIS_PROVIDER=hopper rea decompile /absolute/path/to/program 0x1000
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
For MCP, pass the optional selector on `open_binary`:
|
||||
Review the setup changes and restart your agent. For one-off CLI commands,
|
||||
use `npx rea-agents@latest` followed by the command.
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/absolute/path/to/program",
|
||||
"provider_id": "hopper"
|
||||
}
|
||||
```
|
||||
## How REA works
|
||||
|
||||
Use `--provider`, or `provider_id` in MCP, to choose Hopper or Ghidra for a target. This choice overrides `REA_ANALYSIS_PROVIDER`.
|
||||
Your agent calls REA through MCP to inspect the target and trace relevant code.
|
||||
REA returns findings with their evidence. The agent uses them to ask follow-up
|
||||
questions, explain the behavior, or write and test an implementation.
|
||||
CLI commands use the same workflows.
|
||||
|
||||
With `auto`, REA selects the only available tool that supports the target. If both are available, specify one before opening the target. The session keeps that choice until you explicitly switch or close it; a failure never silently switches tools. Artifact-only analysis can work without a native analysis tool.
|
||||

|
||||
|
||||
Run `rea providers` and `rea capabilities` to check availability and supported operations. Ghidra exposes 22 read-only operations on supported Linux and macOS hosts. GUI controls and annotation changes require Hopper. Windows Ghidra operations remain unavailable.
|
||||
[Open the full-size figure](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
The session also reports active work and cleanup status. If a caller times out, the analysis tool may still be busy; `analysis_activity` reports that state. A `cleanup_incomplete` result identifies resources whose shutdown or removal could not be verified. See [provider selection and analysis profiles](docs/adr/0001-provider-selection-and-analysis-profiles.md) for session, cache, and process-tracking details.
|
||||
<a id="current-status"></a>
|
||||
|
||||
### CLI exit status
|
||||
## What you can analyze
|
||||
|
||||
| Status | Meaning |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `0` | The operation completed. Results may still include warnings, partial evidence, or unresolved questions. |
|
||||
| `1` | The operation could not complete, for example because of invalid input, host permission denial, cancellation, or timeout. Structured output reports the reason when available. |
|
||||
| `128 + N` | The process ended from signal `N`, where the shell or runtime preserves the conventional signal-derived status. |
|
||||
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
|
||||
Additional tools and host support depend on the target:
|
||||
|
||||
`setup` returns `1` for `planned`, `needs_confirmation`, or `needs_human`
|
||||
because configuration is not ready; rerun it after approval or remediation.
|
||||
`doctor` returns `1` when required checks for its readiness scope are unhealthy.
|
||||
Unavailable optional provider prerequisites remain visible as informational
|
||||
diagnostics and do not block setup or unrelated providers. Output format, full envelopes,
|
||||
filters, and token controls never change the operation status.
|
||||
| Target | What REA returns | Requirements and guide |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](https://rea.tools/guides/native/) |
|
||||
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
|
||||
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier; [offline EVM guide](docs/evm-bytecode.md) |
|
||||
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; [application analysis](https://rea.tools/guides/javascript/) |
|
||||
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](https://rea.tools/guides/browser/) |
|
||||
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR; mitmdump on Linux for native mitmproxy captures; [capture guide](docs/web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | Static inspection; [managed-code guide](docs/managed-code-analysis.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
|
||||
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
|
||||
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
|
||||
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
|
||||
|
||||
When REA feeds a shell pipeline, enable `pipefail` so a downstream formatter
|
||||
cannot hide its failure:
|
||||
Static JavaScript and .NET inspection read the supplied files without running
|
||||
the application. Runtime capture runs or interacts with the selected target
|
||||
using your user permissions; each runtime guide describes its effects.
|
||||
|
||||
```bash
|
||||
set -o pipefail
|
||||
rea inventory-artifact ./app.asar --json | jq . > inventory.json
|
||||
```
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
## Current Hopper provider
|
||||
Native formats and host support vary by provider. See
|
||||
[Hopper and Ghidra setup](docs/installation.md#hopper), the
|
||||
[IDA guide](docs/ida-provider.md), and
|
||||
[experimental Windows Ghidra support](docs/windows-ghidra-p0.md).
|
||||
Ghidra also supports [16-bit DOS analysis](docs/ghidra-dos.md).
|
||||
For large binaries, raise its startup deadline with
|
||||
`REA_GHIDRA_STARTUP_TIMEOUT_MS`.
|
||||
For provider selection, see the [CLI guide](docs/cli.md#choose-a-provider).
|
||||
Check [release availability](docs/installation.md#released-package-and-main)
|
||||
for features added since the latest npm release.
|
||||
|
||||
REA starts Hopper when an operation needs it. On macOS, Hopper may bring its window or a dialog to the foreground even though REA requests background startup. Demo or license prompts may need your attention.
|
||||
## Showcases
|
||||
|
||||
Hopper handles one analysis request at a time. Cancelling your wait does not stop work already running inside Hopper; the session reports whether it is still busy. Successful decompilation results are cached until a relevant rename or comment change.
|
||||
### DX-Ball: reconstruct a sound-pan calculation
|
||||
|
||||
Use `rea instructions` when you only need assembly instructions for a function. It avoids decompilation and a whole-program inventory.
|
||||
Follow a sound call into its position-to-pan helper, inspect the instructions,
|
||||
and turn incomplete pseudocode into C. The reconstruction passes 3,205
|
||||
original-x86 cases and reproduces all 63 compiled function bytes.
|
||||
|
||||
Closing a session shuts down REA's bridge and removes its temporary socket directory while preserving a Hopper application you may be using. If cleanup cannot be verified, `close_binary` reports `cleanup_incomplete` and the affected resources.
|
||||
[Read the case study](https://rea.tools/showcase/dx-ball/) ·
|
||||
[Reconstruction repository](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
## Process capture
|
||||
### Notion: trace the Electron clipboard bridge
|
||||
|
||||
Process capture runs the exact executable and scenario declared in the request,
|
||||
with the requested working directory and environment. Filesystem observation
|
||||
paths select what to snapshot. The process runs with your user permissions;
|
||||
Process Capture records behavior and is not a security sandbox.
|
||||
Find the renderer's clipboard API, follow it through preload and IPC into the
|
||||
main process, and inspect the rich clipboard format.
|
||||
|
||||
Capture a scenario or compare two saved Process Capture Evidence records:
|
||||
[Read the case study](https://rea.tools/showcase/notion/)
|
||||
|
||||
```bash
|
||||
rea capture-process ./scenario.json > authority.json
|
||||
rea capture-process ./reconstruction.json > reconstruction.json
|
||||
rea compare-process-captures authority.json reconstruction.json
|
||||
```
|
||||
### TH04: recover a DOS bullet-ring calculation
|
||||
|
||||
The comparison reports each observed dimension separately and identifies the
|
||||
first terminal, interaction, exit, filesystem, or process divergence.
|
||||
See [Process Capture](docs/process-capture.md) for scenario fields, limits, and
|
||||
evidence boundaries.
|
||||
Inspect the original PC-98 game's 16-bit instructions, recover the fixed and
|
||||
aimed angle calculations, and compare the reconstructed C++ with the
|
||||
historical compiler output.
|
||||
|
||||
REA installs a prebuilt PTY backend for supported macOS, Linux, and Windows
|
||||
architectures. If the capability check reports that the backend is unavailable,
|
||||
reinstall REA for the current platform and architecture.
|
||||
[Read the case study](https://rea.tools/showcase/th04/) ·
|
||||
[Reconstruction repository](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
ASAR inventory verifies Electron integrity metadata for both archive entries
|
||||
and `.asar.unpacked` companion files. Integrity failures identify the logical
|
||||
path, declared and calculated SHA-256 values, and whether the entry was
|
||||
unpacked; REA does not silently accept the mismatched artifact. If a supplied
|
||||
ASAR declares unpacked companion bytes that are absent from the local artifact
|
||||
set, REA keeps that occurrence as `unavailable` and continues analyzing the
|
||||
embedded JavaScript instead of treating the missing native/resource bytes as
|
||||
verified or absent.
|
||||
|
||||
## Security model
|
||||
|
||||
Analysis runs locally. REA communicates with Hopper and Ghidra through authenticated private local sockets. Your agent or model provider has its own data policy.
|
||||
|
||||
Runtime requests act on the declared target and lifecycle. Analysis tools and launched targets run with your user permissions, and native UI capture still depends on macOS Accessibility and Screen Recording access. Static JavaScript analysis does not execute extracted modules; use direct browser, Electron, or process capture when runtime behavior is needed.
|
||||
|
||||
Windows Ghidra operations are blocked until REA implements the required process ownership, private-directory permissions, and safe-path checks. Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
|
||||
If you've used REA on something interesting, we'd love to see it. Share your
|
||||
case in an [issue](https://github.com/morluto/rea/issues) or a
|
||||
[pull request](https://github.com/morluto/rea/pulls), including the target,
|
||||
your question, how REA helped, and what you found.
|
||||
|
||||
## FAQ
|
||||
|
||||
<details>
|
||||
<summary><strong>Does Hopper need to be running before I start REA?</strong></summary>
|
||||
|
||||
No. REA starts Hopper when an operation needs it. An already-running Hopper application is also supported.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Why did Hopper appear in front of my other windows?</strong></summary>
|
||||
|
||||
Hopper's launcher internally activates the application. REA requests background startup, but macOS and Hopper may still bring a window or dialog forward. See [Current Hopper provider](#current-hopper-provider).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA include Hopper?</strong></summary>
|
||||
|
||||
No. Setup can install Hopper for you, but Hopper remains separate software with its own license. REA supplies the CLI, MCP server, and workflows that make it usable by agents.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA install or include Ghidra or Java?</strong></summary>
|
||||
|
||||
No. REA connects to an existing Ghidra installation. Run setup after providing the Ghidra and Java paths shown in the [Ghidra section](#ghidra-read-only-analysis-provider).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA upload the app?</strong></summary>
|
||||
|
||||
REA has no hosted analysis service. Current providers analyze artifacts and capture behavior locally. Your agent or model provider may have its own data policy, so review that separately.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Can REA recover the original source code?</strong></summary>
|
||||
|
||||
No decompiler can guarantee the original source. REA gives an agent pseudocode, assembly, symbols, strings, metadata, and relationships that it can use to explain or compatibly recreate observed behavior.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Which agents can use REA?</strong></summary>
|
||||
|
||||
Any agent that can run a local MCP server can use the manual configuration. Setup offers the supported integrations listed in [Installation and setup](docs/installation.md#supported-agents); existing REA registrations are selected by default, and other detected agents require selection.
|
||||
Any agent that supports local MCP servers. Setup configures the
|
||||
[supported agents](docs/installation.md#supported-agents); other clients can use
|
||||
[manual MCP registration](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
## Development
|
||||
<details>
|
||||
<summary><strong>Do I need Hopper, Ghidra or IDA?</strong></summary>
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution checks, and [docs/testing.md](docs/testing.md) for test scopes and real-tool verification. PR CI publishes generated API documentation as the `api-docs` artifact.
|
||||
Deep native analysis uses one of them. Static JavaScript and .NET inspection
|
||||
work without a native analysis engine. Setup can install Hopper after approval;
|
||||
Ghidra and IDA use your existing installations. See [provider setup](docs/installation.md#hopper).
|
||||
|
||||
`npm run verify:agent` evaluates native, JavaScript, managed, and browser investigation tasks through a real local Codex CLI. Its report covers tool selection, repeated calls, token use, completion quality, and handling of permissions and unknowns.
|
||||
</details>
|
||||
|
||||
`npm run evidence:generate` regenerates the managed conformance manifest and Evidence completion ledger from live verification results. `npm run evidence:check` reruns verification and checks for drift. Unsupported claims remain explicit and do not count as passes. See [testing](docs/testing.md) for verification commands and process-cleanup checks.
|
||||
<details>
|
||||
<summary><strong>Do I need to start Hopper first?</strong></summary>
|
||||
|
||||
REA starts Hopper when an operation needs it. On macOS, a first-run dialog may
|
||||
ask you to choose demo mode or activate your license. See
|
||||
[Hopper startup and troubleshooting](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What does installing the skill from skills.sh do?</strong></summary>
|
||||
|
||||
The skill supplies investigation instructions for your agent. Use `rea setup`
|
||||
to register REA's MCP server and install the matching instructions, then restart
|
||||
your agent. See [skill-only installation](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What code does REA return?</strong></summary>
|
||||
|
||||
Native analysis returns pseudocode and assembly. JavaScript/Electron analysis
|
||||
recovers modules and their relationships. Your agent uses these findings to
|
||||
write and test an implementation; the [showcases](#showcases) give worked examples.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA upload my app?</strong></summary>
|
||||
|
||||
REA analyzes targets locally. Your agent receives the tool results, and its
|
||||
model provider has its own data policy.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What should I do if I hit a bug?</strong></summary>
|
||||
|
||||
Update first; a recent release may already fix it.
|
||||
|
||||
For an npm-installed CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
For agent setup through `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
If you're using an agent, complete the [setup refresh](#update-rea) and restart
|
||||
it. Retry the same task. If the problem persists, [open an issue](https://github.com/morluto/rea/issues)
|
||||
with your REA version, target type, steps to reproduce and error output.
|
||||
|
||||
</details>
|
||||
|
||||
## Documentation
|
||||
|
||||
Start with the website's [worked guides](https://rea.tools/guides/).
|
||||
For exact options, prerequisites and result contracts:
|
||||
|
||||
- [Installation and setup](docs/installation.md): agent registration, provider configuration, updates and uninstall.
|
||||
- [Readiness and troubleshooting](docs/installation.md#check-readiness-for-your-task): diagnose one agent or analysis engine.
|
||||
- [CLI and Evidence](docs/cli.md): commands, provider selection, snapshots, import/export and exit statuses.
|
||||
- [MCP contracts](docs/mcp-contracts.md) and [agent prompts](docs/mcp-prompts.md): tool results, sessions and guided investigations.
|
||||
- [Tool catalog](docs/mcp-contracts.md#generated-catalog): generated inventory of tools, providers and CLI commands.
|
||||
- [Roadmap](docs/roadmap.md): planned work and capability trackers.
|
||||
|
||||
Report vulnerabilities through [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
We'd love your help with REA! [Open an issue](https://github.com/morluto/rea/issues) to
|
||||
report a bug or suggest a feature, or [send a pull request](https://github.com/morluto/rea/pulls)
|
||||
to improve the code or docs.
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks,
|
||||
[testing](docs/testing.md) for verification lanes, and the
|
||||
[architecture map](docs/architecture.mermaid) for the project structure.
|
||||
|
||||
## Project links
|
||||
|
||||
[npm](https://www.npmjs.com/package/rea-agents) · [Issues](https://github.com/morluto/rea/issues) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Hopper](https://www.hopperapp.com/) · [Ghidra](https://github.com/NationalSecurityAgency/ghidra)
|
||||
[Website](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Security](SECURITY.md)
|
||||
|
||||
## Star history
|
||||
|
||||
🎉 **30,000 GitHub stars — thank you!**
|
||||
|
||||
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub star history" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
REA provides tools for lawful reverse-engineering research, analysis, and reconstruction. You are responsible for obtaining any required authorization and complying with applicable laws. The project does not endorse illegal or unauthorized use.
|
||||
|
||||
REA is an open-source software project. We have not issued or endorsed any cryptocurrency or token. Tokens using the REA name are not affiliated with the project.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+187
-225
@@ -1,324 +1,286 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **العربية**
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · **العربية** · [فارسی](README_fa.md)
|
||||
|
||||
# REA: هندسة أي شيء عكسيًا
|
||||
# REA: هندسة عكسية لأي شيء
|
||||
|
||||
### استقصِ سلوك التطبيقات والملفات التنفيذية الأصلية باستخدام وكيلك.
|
||||
### خادم MCP واحد للهندسة العكسية للملفات الثنائية والتطبيقات والسلوك أثناء التشغيل.
|
||||
|
||||
**اعثر على ميزة تعجبك. افهم آلية عملها. ابنها بالطريقة التي تريدها.**
|
||||
**وجدت ميزة تعجبك؟ افهم طريقة عملها حتى مستوى الشيفرة الثنائية.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#منصة-أدوات-التحقيق)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [حالة الدعم](#حالة-الدعم) · [من الملف التنفيذي إلى السلوك](#من-الملف-التنفيذي-إلى-السلوك) · [منصة أدوات التحقيق](#منصة-أدوات-التحقيق) · [خطة العمل](#خطة-العمل) · [كيف يعمل؟](#كيف-يعمل)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
<table aria-label="REA community">
|
||||
**[الموقع](https://rea.tools/) · [الأدلة](https://rea.tools/guides/) · [دراسات الحالة](https://rea.tools/showcase/)**
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [كيف يعمل REA](#كيف-يعمل-rea) · [ما الذي يمكنك تحليله](#ما-الذي-يمكنك-تحليله) · [دراسات الحالة](#دراسات-الحالة) · [الأسئلة الشائعة](#الأسئلة-الشائعة) · [التوثيق](#التوثيق)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA يشغّل جسر التحليل داخل Hopper لفحص ملف ثنائي للشيفرة الأصلية" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="مجتمع REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>انضم إلى مجتمع الهندسة العكسية</strong>
|
||||
</a><br />
|
||||
<sub>Discord · أسئلة وأجوبة · شارك أعمالك</sub>
|
||||
<sub>Discord · أسئلة وأجوبة · مشاركة النتائج</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA يشغّل جسر التحليل داخل Hopper لفحص ملف تنفيذي أصلي" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
هل وجدت في تطبيق ميزة تريدها في منتجك؟ اطلب من وكيلك استقصاءها باستخدام REA. يستطيع فحص التطبيق من دون شيفرته المصدرية، وشرح طريقة عمل الميزة وعرض الأدلة، ثم بناء نسخة تناسب مشروعك.
|
||||
هل وجدت في تطبيق ميزة تريد إضافتها إلى منتجك؟ اطلب من وكيلك استقصاءها باستخدام REA. يستطيع فحص التطبيق من دون شيفرته المصدرية، وشرح طريقة عمل الميزة وعرض الأدلة، ثم بناء نسخة تناسب مشروعك.
|
||||
|
||||
يوفر REA أدوات لتحليل الملفات التنفيذية الأصلية وتطبيقات JavaScript وElectron وتجميعات .NET والمواقع. يمكنك استخدامها من وكيلك أو من الطرفية. يعمل التحليل محليًا، وتتضمن النتائج الأدلة والقيود.
|
||||
يربط REA وكيلك بأدوات لفحص الملفات الثنائية للشيفرة الأصلية، وتطبيقات JavaScript وElectron، وتجميعات .NET، والمواقع. يمكنك أيضًا استخدام الأدوات نفسها من الطرفية. يجري التحليل محليًا، وتتضمن النتائج الأدلة والقيود التي تستند إليها كل خلاصة.
|
||||
|
||||
يضبط Setup إعدادات الوكيل ويربطه بتثبيت موجود من Hopper أو Ghidra. ويمكنه تثبيت Hopper بعد موافقتك.
|
||||
يسجّل الإعداد REA لدى وكيلك ويثبّت تعليمات سير العمل المطابقة. يمكن لتحليل الشيفرة الأصلية استخدام تثبيت موجود من Hopper أو Ghidra؛ ويمكن للإعداد أيضًا تثبيت Hopper اختياريًا بعد موافقتك. لا يحتاج التحليل الثابت لـ JavaScript إلى أي من المحركين.
|
||||
|
||||
## اطلب من وكيلك مباشرة
|
||||
|
||||
بعد [الإعداد](#البدء-السريع)، أعد تشغيل الوكيل ثم اطلب:
|
||||
|
||||
```text
|
||||
استقصِ طريقة عمل البحث في تطبيق Notes، واعرض الأدلة، ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
```
|
||||
|
||||
استبدل Notes بالتطبيق الذي تريد فهمه، أو اطلب نظرة عامة أولًا.
|
||||
|
||||
## من الملف التنفيذي إلى السلوك
|
||||
|
||||
| فك الترجمة | الفهم | إعادة الإنشاء |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| استخرج الشيفرة شبه المصدرية، والتعليمات، والسلاسل النصية، والرموز | تتبع تدفق التحكم والمراجع المتبادلة والاستدعاءات والأنواع | حوّل ما تعلمه الوكيل إلى ميزة تناسب تقنياتك وواجهتك ومتطلباتك |
|
||||
|
||||
يوضح REA كيف وصل إلى نتائجه. وهو لا يدّعي استعادة الشيفرة المصدرية الأصلية أو استنساخ التطبيق تلقائيًا.
|
||||
|
||||
## لماذا REA؟
|
||||
|
||||
| | |
|
||||
| -------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **مصمم للوكلاء** | اسأل عما يفعله تطبيق مترجم ودع الوكيل يجمع الأدلة بدلًا من التخمين. |
|
||||
| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو وكيل البرمجة. |
|
||||
| **يتولى التعقيد** | يدير REA إعداد الأدوات وفتح التطبيق واستمرار الاستقصاء والتنظيف بعد الانتهاء. |
|
||||
| **سير عمل كامل** | انتقل من النظرة الأولى إلى الشيفرة شبه المصدرية وعلاقات الاستدعاء والأنواع وأدلة التنفيذ. |
|
||||
| **محلي حسب التصميم** | يجري التحليل على جهازك المدعوم ولا يرفع REA الملف التنفيذي إلى خدمة تحليل مستضافة. |
|
||||
| **يحافظ على السياق** | استقصِ عدة ملفات تنفيذية من دون بدء التحليل من جديد عند كل سؤال. |
|
||||
> **[زر موقع REA](https://rea.tools/)** للاطلاع على تعليمات الإعداد والأدلة المصوّرة ودراسات الحالة الفعلية.
|
||||
|
||||
## البدء السريع
|
||||
|
||||
### تشغيل الإعداد (موصى به)
|
||||
### إعداد وكيلك
|
||||
|
||||
اربط REA بوكيلك:
|
||||
بعد تثبيت Node.js وnpm، نفّذ:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
يعرض Setup أولاً قائمة متعددة الاختيار للوكلاء الذين تريد ربطهم. تكون تسجيلات REA الموجودة محددة افتراضياً، ولا يؤدي اكتشاف عميل جديد وحده إلى تحديده؛ ويمكنك اختيار عميل غير مهيأ. راجع المسارات والتغييرات المحددة قبل الموافقة. يثبت Setup سير عمل REA للوكلاء المحددين افتراضياً. Hopper خيار منفصل يتطلب موافقة مستقلة، ويمكن أيضاً حفظ مسارات تثبيت Ghidra الموجودة.
|
||||
اختر الوكلاء، وراجع التغييرات المقترحة، ثم وافق عليها. يضيف الإعداد خادم MCP الخاص بـ REA وتعليمات سير العمل المطابقة، مع الاحتفاظ بنسخ احتياطية من الإعدادات الموجودة. أعد تشغيل وكيلك بعد ذلك.
|
||||
|
||||
يعرض التغييرات قبل تطبيقها وينشئ نسخة احتياطية من الإعدادات الموجودة. راجع [دليل التثبيت والإعداد](docs/installation.md) للمتطلبات والخيارات الإضافية.
|
||||
يدعم الإعداد Claude Code وCodex وCursor وGemini CLI وGrok Build و[وكلاء آخرين](docs/installation.md#supported-agents). راجع [التثبيت والإعداد](docs/installation.md) لتهيئة موفّري التحليل وتسجيل MCP يدويًا.
|
||||
|
||||
### الاستخدام مع وكيل
|
||||
### اطلب من وكيلك
|
||||
|
||||
بعد الإعداد، أعد تشغيل الوكيل واشرح التطبيق أو الميزة التي تريد استقصاءها. يدعم REA Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن للوكلاء الآخرين استخدام إعداد MCP أدناه.
|
||||
|
||||
يمكن تشغيل Hopper في الوضع التجريبي. إذا ظهرت رسالة عند التشغيل الأول، فاختر التجربة أو أدخل ترخيصًا موجودًا.
|
||||
|
||||
### الاستخدام من الطرفية
|
||||
|
||||
بعد الإعداد، شغّل:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
```text
|
||||
استقصِ طريقة عمل البحث في تطبيق Notes، واعرض الأدلة، ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
```
|
||||
|
||||
### تثبيت أمر rea
|
||||
استبدل Notes بالتطبيق المستهدف وحدّد الميزة التي تريد فهمها.
|
||||
|
||||
ثبت واجهة سطر الأوامر:
|
||||
### استخدام الطرفية
|
||||
|
||||
افحص مجلد تطبيق JavaScript/Electron بعد فك حزمه، أو ملف ASAR:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
يجب تثبيت Node.js وnpm مسبقًا. عند تشغيله من طرفية، يثبت البرنامج `rea` ويبدأ الإعداد.
|
||||
تتضمن النتيجة الوحدات وعمليات الاستيراد وحدود Electron والأدلة المتعلقة بها. استبدل المسار بمسار هدفك، مثل `"D:/apps/example"` على Windows.
|
||||
|
||||
يمكنك بدلًا من ذلك التثبيت باستخدام npm ثم تشغيل الإعداد:
|
||||
لتثبيت أمر `rea` للاستخدام المعتاد:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
rea --help
|
||||
```
|
||||
|
||||
### المتطلبات
|
||||
لتحليل الشيفرة الأصلية، هيّئ موفّرًا أولًا. راجع [دليل CLI وEvidence](docs/cli.md) لأوامر تحليل الشيفرة الأصلية، واختيار الموفّر، واللقطات، والاستخدام في السكربتات.
|
||||
|
||||
- macOS 12 أو أحدث
|
||||
- Ubuntu 24.04+ أو Fedora 41+ أو Arch Linux بنواة 64 بت
|
||||
- Node.js 22.19+ أو 24.11+ وnpm
|
||||
### تحديث REA
|
||||
|
||||
يتطلب تحليل الملفات التنفيذية الأصلية Hopper أو Ghidra. Hopper برنامج منفصل، وللوضع التجريبي قيود يحددها المورّد، لكن الترخيص المدفوع ليس إلزاميًا.
|
||||
يتطور REA بسرعة، وتتضمن الإصدارات الجديدة إصلاحات متكررة للأخطاء. حافظ على تحديث نسختك.
|
||||
|
||||
يدعم Ghidra نظامي Linux x64 وmacOS x64/arm64. ثبت Ghidra 12.1.4 وJDK 21 الكامل بنواة 64 بت بصورة منفصلة، ثم اضبط REA لاستخدامهما. يحتاج macOS أيضًا إلى أداة فك الترجمة الأصلية المطابقة لمعمارية الجهاز.
|
||||
|
||||
يستطيع Setup التحقق من التثبيت وحفظ المسارات؛ ولا يثبت أو يحدّث Ghidra أو Java أو Node.js أو npm أو Homebrew.
|
||||
|
||||
تحليل Ghidra على Windows غير متاح. لم تُنفذ بعد ضوابط ملكية العمليات وأذونات المجلدات الخاصة والتحقق الآمن من المسارات. حتى التثبيت الصحيح لـ Ghidra وJava لا يفعّل التحليل. راجع [Windows Ghidra P0](docs/windows-ghidra-p0.md).
|
||||
|
||||
### تشخيص المشكلات
|
||||
|
||||
يشخّص `npx -y rea-agents@latest doctor` الجهاز والاعتماديات وأدوات التحليل وإعدادات الوكيل من دون تغييرها. أضف `--json` للحصول على تشخيص منظم.
|
||||
|
||||
مسار Hopper الافتراضي على Linux هو `/opt/hopper/bin/Hopper`. استخدم `HOPPER_LAUNCHER_PATH` للمسارات الأخرى. إذا كان الملف موجودًا لكن محرك التحليل لا يظهر، فشغّل `ldd /opt/hopper/bin/Hopper | grep 'not found'` للتحقق من المكتبات الناقصة. راجع [دليل Hopper](docs/installation.md#hopper).
|
||||
|
||||
### التحديث والإزالة
|
||||
|
||||
- يحدّث `rea update` تثبيت REA الحالي.
|
||||
- يزيل `rea uninstall` تسجيلات الوكلاء وملفات سير العمل التي يديرها REA، ويترك Hopper مثبتًا.
|
||||
- يزيل `rea uninstall --purge-data` أيضًا ذاكرة REA المؤقتة وحالته. استخدمه فقط إذا أردت حذف هذه البيانات.
|
||||
|
||||
## حالة الدعم
|
||||
|
||||
يوفر REA عبر CLI وMCP تحليل الملفات التنفيذية الأصلية وتطبيقات JavaScript وElectron وتجميعات .NET والمواقع. تعتمد العمليات المتاحة على الجهاز والهدف وأداة التحليل المختارة.
|
||||
|
||||
- يدعم Hopper التحليل الأصلي والتعليقات؛ ويختلف سلوك واجهته الرسومية حسب المنصة.
|
||||
- يقدم Ghidra على Linux x64 وmacOS x64/arm64 عددًا قدره 22 عملية للقراءة فقط: الجرد والبحث وفك الترجمة والتعليمات والاستدعاءات والمراجع والأنواع. لا يقدم عمليات واجهة رسومية أو تعديل.
|
||||
- لسير عمل المتصفح وElectron وتشغيل العمليات متطلبات مستقلة للإعداد والموافقة وإدارة دورة الحياة. راجع [English README](README.md#current-status) للتفاصيل الكاملة.
|
||||
- تحليل Ghidra على Windows غير مفعّل. تابع التقدم في [#527](https://github.com/morluto/rea/issues/527).
|
||||
|
||||
## استقصاء كامل بطلب واحد
|
||||
|
||||
بعد الإعداد، يمكنك أن تطلب من وكيلك:
|
||||
|
||||
> افتح التطبيق، واكتشف آلية عمل البحث من دون اتصال، واشرح تدفق التحكم، ثم ابنِ نسخة لمشروعي باستخدام TypeScript وSQLite.
|
||||
|
||||
يمكن للوكيل تحويل ذلك إلى سير عمل قابل للتحقق:
|
||||
|
||||
```text
|
||||
فتح الهدف
|
||||
→ انتظار اكتمال تحليل Hopper
|
||||
→ جرد المعماريات والرموز والسلاسل النصية
|
||||
→ تحديد نقاط الدخول والدوال المهمة
|
||||
→ تتبع الاستدعاءات والمراجع المتبادلة وتدفق التحكم
|
||||
→ فك ترجمة التنفيذ ذي الصلة
|
||||
→ تلخيص السلوك والقيود والحالات الطرفية
|
||||
→ بناء الميزة بما يلائم تقنيات المشروع ومتطلباته
|
||||
```
|
||||
|
||||
## ما الذي يمكنك فعله؟
|
||||
|
||||
### فهم تطبيق لا تملك مصدره
|
||||
|
||||
اكشف نقاط الدخول، وأسماء الفئات، والسلاسل النصية، والأطر المستخدمة، ومسارات الميزات من الملف التنفيذي وحده.
|
||||
|
||||
### بناء ميزة تعجبك بطريقتك
|
||||
|
||||
استقصِ الميزة، وافهم سلوكها، ثم ابنِ نسخة ملائمة لمنتجك وتقنياتك ومتطلباتك.
|
||||
|
||||
### توثيق بروتوكول أو تنسيق غير موثق
|
||||
|
||||
تتبع الموزعات، والثوابت، والمقارنات، والقراءة والكتابة لفهم الرسائل وتنسيقات الملفات وآلات الحالات.
|
||||
|
||||
### إعادة إنشاء سلوك متوافق
|
||||
|
||||
حوّل منطقًا مفكوك الترجمة إلى مواصفات واضحة، ثم نفذ بديلًا نظيفًا تحكمه اختبارات السلوك الملحوظ.
|
||||
|
||||
### تدقيق منطق أمني حساس
|
||||
|
||||
افحص التحقق من الإدخال، والتشفير، والصلاحيات، والتخزين، ومسارات الأخطاء من دون رفع الملف التنفيذي إلى خدمة بعيدة.
|
||||
|
||||
## منصة أدوات التحقيق
|
||||
|
||||
| عائلة الأدوات | العدد | الاستخدام |
|
||||
| --------------------- | ----: | -------------------------------------------------------------------------------------- |
|
||||
| فحص الملفات التنفيذية | 39 | الدوال والشيفرة شبه المصدرية والتعليمات والسلاسل والرموز والمراجع والتعليقات |
|
||||
| التحليل المركب | 14 | النظرة العامة وتحليل الدوال وفك الترجمة الدفعي ومخطط الاستدعاء وفحص Swift وObjC |
|
||||
| أدوات macOS الأصلية | 7 | بيانات Mach-O والتوقيعات وplist والمعماريات واستعادة أسماء Swift |
|
||||
| الملفات والحزم | 5 | فحص الدلائل والحزم وInterface Builder وموارد Apple والاستخراج |
|
||||
| .NET PE/CLI | 7 | هوية التجميع والبيانات الوصفية وCIL والاستدعاءات الأصلية والمقارنة واستيراد إعادة البناء |
|
||||
| مراقبة المتصفح | 9 | الصفحات والبرامج النصية وخرائط المصدر وWebMCP والصور ومقارنة الالتقاط |
|
||||
| تحليل Electron | 5 | مراقبة الصفحات وبنية التطبيق وربط النتائج الثابتة بنتائج التشغيل |
|
||||
| وقت تشغيل JavaScript | 2 | الاتصال بهدف Node/Electron Inspector موجود لمراقبة البرامج وسياقات التنفيذ |
|
||||
| سير عمل التطبيقات | 7 | تتبع الميزات ومقارنة الإصدارات وبنية قيم الإرجاع والتحقق من إعادة الإنشاء |
|
||||
| جلسة الملف التنفيذي | 21 | تبديل الأهداف وحفظ الأدلة ومقارنة العمليات والدوال وتسجيل الأسئلة غير المحسومة |
|
||||
|
||||
## خطة العمل
|
||||
|
||||
يشمل العمل التالي توسيع التحقق من الأهداف الأصلية، ومقارنة إصدارات JavaScript و.NET، ومراقبة وقت التشغيل والتحقق من إعادة الإنشاء. يتيح Setup بالفعل اختيار تكامل الوكيل وتثبيت Hopper. تثبيت أدوات تحليل أخرى ما زال عملًا لاحقًا. راجع [خطة التثبيت](docs/roadmap.md) و[تقييم أدوات التحليل](docs/provider-evaluation.md).
|
||||
|
||||
## استخدام REA مع وكلاء برمجة آخرين
|
||||
|
||||
يدعم Setup Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن لأي وكيل يدعم خوادم MCP المحلية الاتصال باستخدام الإعداد التالي:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@4.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## كيف يعمل؟
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Human[المستخدم] --> Agent[وكيل البرمجة]
|
||||
Agent --> REA[REA]
|
||||
Human -->|CLI| REA
|
||||
REA --> Hopper[عمليات تحليل Hopper]
|
||||
Hopper --> App[تطبيقك]
|
||||
REA --> Ghidra[تحليل Ghidra للقراءة فقط<br/>جرد + تحليل الدوال]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
يستخدم CLI وخادم MCP سير عمل التحليل نفسه. تحرر أوامر الطرفية جلسة الجسر الخاصة بها عند انتهائها، بينما يمكن لجلسة الوكيل الحفاظ على الاتصال أثناء الاستقصاء. إغلاق جلسة REA لا يغلق تطبيق Hopper الذي يستخدمه المستخدم.
|
||||
|
||||
## أوامر CLI
|
||||
|
||||
سير عمل الوكيل أعلاه هو أسهل طريقة لاستخدام REA. للحصول على نظرة سريعة على تطبيق من الطرفية:
|
||||
لواجهة CLI المثبّتة عبر npm:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
|
||||
rea update
|
||||
```
|
||||
|
||||
شغّل `npx -y rea-agents@latest --help` لمعرفة خيارات فك الترجمة المباشر والخيارات الأخرى.
|
||||
لتحديث تسجيلات الوكلاء والمهارة، نفّذ أمر الإعداد الذي يعرضه التحديث.
|
||||
|
||||
يمكن لـ REA فتح مجلد `.app` على Mac مباشرة. إذا لم يعثر الوكيل على التطبيق، فأخبره بمكان تثبيته.
|
||||
إذا كنت تستخدم `npx`، فحدّث إعداد وكيلك بالأمر التالي:
|
||||
|
||||
## سلوك Hopper
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
يشغّل REA برنامج Hopper عند الحاجة. على macOS قد تظهر نافذته في المقدمة أو يحتاج أحد الحوارات إلى استجابة منك. لا يمكن ضمان بقاء النافذة في الخلفية.
|
||||
راجع تغييرات الإعداد وأعد تشغيل وكيلك. لأوامر CLI التي تنفّذها مرة واحدة، استخدم `npx rea-agents@latest` متبوعًا بالأمر المطلوب.
|
||||
|
||||
يحدد REA تنسيق الهدف ومعماريته لتجنب حوارات الاختيار المعتادة. عند إغلاق الجلسة، يوقف الجسر وينظف موارده الخاصة، لكنه لا يغلق تطبيق Hopper الذي تستخدمه.
|
||||
## كيف يعمل REA
|
||||
|
||||
## الأمان والخصوصية
|
||||
يستدعي وكيلك REA عبر MCP لفحص الهدف وتتبع الشيفرة ذات الصلة. يعيد REA النتائج مع أدلتها. يستخدمها الوكيل لطرح أسئلة متابعة، أو شرح السلوك، أو كتابة تنفيذ واختباره. تستخدم أوامر CLI سير العمل نفسه.
|
||||
|
||||
- يبقى تحليل الملف التنفيذي محليًا بين REA ومزوّد التحليل المختار.
|
||||
- لا يرفع REA الأهداف إلى خدمة مستضافة.
|
||||
- تستخدم جلسات Ghidra مشروعًا مؤقتًا معزولًا ولا تفتح مشاريع Ghidra التي يملكها المستخدم أو تعدّلها.
|
||||
- راجع الملفات التنفيذية غير الموثوقة واعزلها كما تفعل مع أي مدخل أصلي قد يكون ضارًا.
|
||||
- أبلغ عن الثغرات وفق [سياسة الأمان](SECURITY.md)، وليس عبر قضية عامة.
|
||||

|
||||
|
||||
[افتح الشكل بحجمه الكامل](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## ما الذي يمكنك تحليله
|
||||
|
||||
يتطلب REA إصدار Node.js 22.x (>=22.19) أو 24.x (>=24.11) أو 26+، إضافة إلى npm. تعتمد الأدوات الإضافية والأنظمة المضيفة المدعومة على الهدف:
|
||||
|
||||
| الهدف | ما يعيده REA | المتطلبات والدليل |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| الملفات الثنائية للشيفرة الأصلية | شيفرة شبه برمجية، وتعليمات تجميع، وسلاسل نصية، ورموز، واستدعاءات، ومراجع | Hopper أو Ghidra أو IDA؛ [تحليل الشيفرة الأصلية](https://rea.tools/guides/native/) |
|
||||
| بنية ELF دون تشغيل | الأقسام والمقاطع والرموز ومعلومات إعادة التموضع الأصلية، وآليات الحماية المحتملة المستدلّ عليها بالفحص الثابت | pwntools يوفّره المستدعي على Linux x64؛ [تشخيص الملفات الثنائية](docs/binary-diagnostics.md) |
|
||||
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا؛ [دليل EVM دون تشغيل](docs/evm-bytecode.md) |
|
||||
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | الوحدات، وعمليات الاستيراد، وخرائط المصدر، والمسارات، وIPC، وعلاقات إضافات الشيفرة الأصلية | Node.js وnpm؛ [تحليل التطبيقات](https://rea.tools/guides/javascript/) |
|
||||
| المواقع | بنية الصفحة، والسكربتات، وملاحظات الشبكة، ولقطات الشاشة المطلوبة | متصفح من عائلة Chrome؛ [تحليل المتصفح](https://rea.tools/guides/browser/) |
|
||||
| تسجيلات الشبكة المحفوظة | الطلبات، والاستجابات، ومحتويات الحمولة المتاحة، ومواضعها في المصدر | HAR؛ وmitmdump على Linux لتسجيلات mitmproxy بصيغتها الأصلية؛ [دليل تسجيلات الشبكة](docs/web-network-captures.md) |
|
||||
| تجميعات .NET | البيانات الوصفية، وتعليمات CIL، واعتماديات الشيفرة الأصلية المعلنة، ومقارنات البناء | فحص ثابت؛ [دليل الشيفرة المُدارة](docs/managed-code-analysis.md) |
|
||||
| حزم Android APK | تصريحات ملف manifest، والأصناف، والدوال الناتجة عن فك الترجمة، والمراجع | JADX دون واجهة رسومية وJDK كامل على Linux/macOS؛ [دليل Android](docs/android-analysis.md) |
|
||||
| البرامج الثابتة | المناطق، ونتائج الاستخراج، وما يُمرّر إلى تحليل الشيفرة الأصلية | Binwalk / Unblob على Linux؛ [دليل البرامج الثابتة](docs/firmware-analysis.md) |
|
||||
| الحزم والموارد | قوائم الملفات، والبصمات، وملفات plist، وبنية حزم Apple، والموارد المستخرجة | [دليل تحليل الملفات وJavaScript](docs/javascript-artifact-reconstruction.md)، [تطبيقات Apple](docs/apple-application-analysis.md) |
|
||||
| سلوك العمليات | مخرجات الطرفية، والتفاعلات، وملاحظات الخروج ونظام الملفات، ومقارنات التشغيل | Linux/macOS مع PTY أصلي؛ [تسجيل سلوك العمليات](docs/process-capture.md) |
|
||||
|
||||
يقرأ الفحص الثابت لـ JavaScript و.NET الملفات المقدّمة دون تشغيل التطبيق. يشغّل التسجيل أثناء التنفيذ الهدف المحدد أو يتفاعل معه بصلاحيات مستخدمك؛ ويشرح كل دليل للتسجيل أثناء التنفيذ آثاره.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
تختلف صيغ ملفات الشيفرة الأصلية والأنظمة المضيفة المدعومة باختلاف الموفّر. راجع [إعداد Hopper وGhidra](docs/installation.md#hopper)، و[دليل IDA](docs/ida-provider.md)، و[دعم Ghidra التجريبي على Windows](docs/windows-ghidra-p0.md). يدعم Ghidra أيضًا [تحليل DOS ذي 16 بت](docs/ghidra-dos.md). لاختيار الموفّر، راجع [دليل CLI](docs/cli.md#choose-a-provider). تحقّق من [توفر الميزات في الإصدارات](docs/installation.md#released-package-and-main) للميزات المضافة بعد أحدث إصدار على npm.
|
||||
|
||||
## دراسات الحالة
|
||||
|
||||
### DX-Ball: إعادة بناء حساب توزيع الصوت بين القناتين
|
||||
|
||||
تتبّع استدعاء صوت إلى الدالة المساعدة التي تحوّل الموضع إلى توزيع بين القناتين، وافحص التعليمات، وحوّل الشيفرة شبه البرمجية غير المكتملة إلى C. تجتاز إعادة البناء 3,205 حالات اختبار على x86 الأصلي، وتعيد إنتاج جميع بايتات الدالة المترجمة البالغ عددها 63 بايتًا.
|
||||
|
||||
[اقرأ دراسة الحالة](https://rea.tools/showcase/dx-ball/) ·
|
||||
[مستودع إعادة البناء](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: تتبّع جسر الحافظة في Electron
|
||||
|
||||
اعثر على API الحافظة في عملية العرض، وتتبّعه عبر preload وIPC إلى العملية الرئيسية، وافحص صيغة الحافظة ذات التنسيق الغني.
|
||||
|
||||
[اقرأ دراسة الحالة](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: استعادة حساب حلقة المقذوفات في DOS
|
||||
|
||||
افحص تعليمات اللعبة الأصلية على PC-98 ذات 16 بت، واستعد حساب الزوايا الثابتة والموجّهة نحو الهدف، وقارن شيفرة C++ المعاد بناؤها بمخرجات المترجم المستخدم آنذاك.
|
||||
|
||||
[اقرأ دراسة الحالة](https://rea.tools/showcase/th04/) ·
|
||||
[مستودع إعادة البناء](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
إذا استخدمت REA لفحص هدف مثير للاهتمام، فنود الاطلاع عليه. شارك حالتك في [issue](https://github.com/morluto/rea/issues) أو [pull request](https://github.com/morluto/rea/pulls)، مع ذكر الهدف، وسؤالك، وكيف ساعدك REA، وما توصلت إليه.
|
||||
|
||||
## الأسئلة الشائعة
|
||||
|
||||
<details>
|
||||
<summary><strong>هل يتضمن REA محرك فك ترجمة خاصًا به؟</strong></summary>
|
||||
<summary><strong>أي وكلاء يمكنهم استخدام REA؟</strong></summary>
|
||||
|
||||
لا. يستطيع Setup تثبيت Hopper، لكنه يظل برنامجًا منفصلًا بترخيص خاص به. يوفر REA واجهة CLI وخادم MCP وسير العمل المخصص للوكلاء.
|
||||
أي وكيل يدعم خوادم MCP المحلية. يهيّئ الإعداد [الوكلاء المدعومين](docs/installation.md#supported-agents)؛ ويمكن للعملاء الآخرين استخدام [تسجيل MCP يدويًا](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>هل يجب أن يكون Hopper مفتوحًا مسبقًا؟</strong></summary>
|
||||
<summary><strong>هل أحتاج إلى Hopper أو Ghidra أو IDA؟</strong></summary>
|
||||
|
||||
لا. يستطيع REA تشغيله. لكن Hopper قد يصبح مرئيًا أو يطلب إدخالًا عند الحاجة إلى قرار تفاعلي.
|
||||
يستخدم تحليل الشيفرة الأصلية المتعمق أحدها. يعمل الفحص الثابت لـ JavaScript و.NET دون محرك لتحليل الشيفرة الأصلية. يستطيع الإعداد تثبيت Hopper بعد الموافقة؛ أما Ghidra وIDA فيستخدمان تثبيتاتك الموجودة. راجع [إعداد الموفّرين](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>هل يعمل REA على Linux أو Windows؟</strong></summary>
|
||||
<summary><strong>هل أحتاج إلى تشغيل Hopper أولًا؟</strong></summary>
|
||||
|
||||
يدعم REA نظام macOS 12+ وUbuntu 24.04+ وFedora 41+ وArch Linux بنواة 64 بت. يعمل تحليل Ghidra للقراءة فقط على Linux x64 وmacOS x64/arm64 مع Ghidra 12.1.4 وJDK 21 الكامل. تحليل Ghidra على Windows غير متاح بسبب الضوابط الأصلية غير المنفذة؛ راجع [Windows Ghidra P0](docs/windows-ghidra-p0.md).
|
||||
يشغّل REA برنامج Hopper عندما تحتاج العملية إليه. على macOS، قد تظهر عند التشغيل الأول نافذة تطلب اختيار وضع العرض التجريبي أو تفعيل الترخيص. راجع [تشغيل Hopper واستكشاف الأخطاء](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>هل يمكنني تحليل قواعد Hopper المحفوظة؟</strong></summary>
|
||||
<summary><strong>ماذا يفعل تثبيت المهارة من skills.sh؟</strong></summary>
|
||||
|
||||
نعم. يدعم وقت التشغيل الملفات التنفيذية وقواعد `.hop`.
|
||||
توفّر المهارة تعليمات الاستقصاء لوكيلك. استخدم `rea setup` لتسجيل خادم MCP الخاص بـ REA وتثبيت التعليمات المطابقة، ثم أعد تشغيل وكيلك. راجع [تثبيت المهارة وحدها](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
## التطوير
|
||||
<details>
|
||||
<summary><strong>ما الشيفرة التي يعيدها REA؟</strong></summary>
|
||||
|
||||
راجع [CONTRIBUTING.md](CONTRIBUTING.md) لإعداد بيئة التطوير وبنية المشروع والاختبارات وإرشادات الإصدار.
|
||||
يعيد تحليل الشيفرة الأصلية شيفرة شبه برمجية وتعليمات تجميع. يستعيد تحليل JavaScript/Electron الوحدات والعلاقات بينها. يستخدم وكيلك هذه النتائج لكتابة تنفيذ واختباره؛ وتقدّم [دراسات الحالة](#دراسات-الحالة) أمثلة عملية.
|
||||
|
||||
## المشروع
|
||||
</details>
|
||||
|
||||
- [حزمة npm](https://www.npmjs.com/package/rea-agents)
|
||||
- [GitHub](https://github.com/morluto/rea)
|
||||
- [القضايا والطلبات](https://github.com/morluto/rea/issues)
|
||||
- [Hopper Disassembler](https://www.hopperapp.com/)
|
||||
- [سياسة الأمان](SECURITY.md)
|
||||
<details>
|
||||
<summary><strong>هل يرفع REA تطبيقي؟</strong></summary>
|
||||
|
||||
يحلّل REA الأهداف محليًا. يتلقى وكيلك نتائج الأدوات، ولموفّر النموذج الذي يستخدمه سياسة بيانات خاصة به.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>ماذا أفعل إذا واجهت خطأ؟</strong></summary>
|
||||
|
||||
حدّث أولًا؛ فقد يكون إصدار حديث قد أصلح المشكلة بالفعل.
|
||||
|
||||
لواجهة CLI المثبّتة عبر npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
لإعداد الوكيل عبر `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
إذا كنت تستخدم وكيلًا، فأكمل [تحديث الإعداد](#تحديث-rea) وأعد تشغيله. أعد محاولة المهمة نفسها. إذا استمرت المشكلة، [افتح issue](https://github.com/morluto/rea/issues) يتضمن إصدار REA، ونوع الهدف، وخطوات إعادة الإنتاج، ومخرجات الخطأ.
|
||||
|
||||
</details>
|
||||
|
||||
## التوثيق
|
||||
|
||||
ابدأ بـ [الأدلة العملية](https://rea.tools/guides/) على الموقع. للخيارات الدقيقة والمتطلبات وعقود النتائج:
|
||||
|
||||
- [التثبيت والإعداد](docs/installation.md): تسجيل الوكلاء، وتهيئة الموفّرين، والتحديثات، وإلغاء التثبيت.
|
||||
- [الجاهزية واستكشاف الأخطاء](docs/installation.md#check-readiness-for-your-task): تشخيص وكيل أو محرك تحليل محدد.
|
||||
- [CLI وEvidence](docs/cli.md): الأوامر، واختيار الموفّر، واللقطات، والاستيراد/التصدير، وحالات الخروج.
|
||||
- [عقود MCP](docs/mcp-contracts.md) و[تعليمات الوكلاء](docs/mcp-prompts.md): نتائج الأدوات، والجلسات، والاستقصاءات الموجّهة.
|
||||
- [كتالوج الأدوات](docs/mcp-contracts.md#generated-catalog): قائمة الأدوات والموفّرين وأوامر CLI المولّدة أثناء البناء.
|
||||
- [خطة العمل](docs/roadmap.md): الأعمال المخطط لها ومتابعة القدرات.
|
||||
|
||||
أبلغ عن الثغرات وفق [SECURITY.md](SECURITY.md).
|
||||
|
||||
## المساهمة
|
||||
|
||||
نرحّب بمساعدتك في تطوير REA! [افتح issue](https://github.com/morluto/rea/issues) للإبلاغ عن خطأ أو اقتراح ميزة، أو [أرسل pull request](https://github.com/morluto/rea/pulls) لتحسين الشيفرة أو التوثيق.
|
||||
|
||||
راجع [CONTRIBUTING.md](CONTRIBUTING.md) لإعداد بيئة التطوير والفحوص، و[دليل الاختبار](docs/testing.md) لمسارات التحقق، و[خريطة البنية](docs/architecture.mermaid) لهيكل المشروع.
|
||||
|
||||
## روابط المشروع
|
||||
|
||||
[الموقع](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [الأمان](SECURITY.md)
|
||||
|
||||
## سجل النجوم
|
||||
|
||||
🎉 **30,000 نجمة على GitHub — شكرًا لكم!**
|
||||
|
||||
شكرًا لكل من يستخدم REA، ويبلّغ عن الأخطاء، ويقترح ميزات، ويختبر إصدارات البناء، ويساهم بالإصلاحات.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="سجل نجوم REA على GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## إخلاء المسؤولية
|
||||
|
||||
يوفّر REA أدوات لأبحاث الهندسة العكسية والتحليل وإعادة البناء المشروعة. أنت مسؤول عن الحصول على أي تفويض مطلوب والالتزام بالقوانين المعمول بها. لا يؤيد المشروع الاستخدام غير القانوني أو غير المصرّح به.
|
||||
|
||||
## الترخيص
|
||||
|
||||
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · **Deutsch** · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Alles reverse-engineeren
|
||||
|
||||
### Ein MCP für das Reverse Engineering von Binärdateien, Anwendungen und Laufzeitverhalten.
|
||||
|
||||
**Eine Funktion gefällt dir? Verstehe, wie sie funktioniert – bis hinunter auf die Binärebene.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Anleitungen](https://rea.tools/guides/) · [Fallstudien](https://rea.tools/showcase/)**
|
||||
|
||||
[Schnellstart](#schnellstart) · [So funktioniert REA](#so-funktioniert-rea) · [Was du analysieren kannst](#was-du-analysieren-kannst) · [Fallstudien](#fallstudien) · [Häufige Fragen](#häufige-fragen) · [Dokumentation](#dokumentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA startet seine Analysebrücke in Hopper und untersucht eine native Binärdatei" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA-Community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Der Reverse-Engineering-Community beitreten</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Fragen und Antworten · Ergebnisse teilen</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Du hast in einer App eine Funktion entdeckt, die du in dein eigenes Produkt übernehmen möchtest? Lass deinen Agenten sie mit REA untersuchen. Er kann die App ohne ihren Quellcode analysieren, die Funktionsweise erklären, Belege zeigen und eine passende Umsetzung für dein Projekt entwickeln.
|
||||
|
||||
REA verbindet deinen Agenten mit Werkzeugen zum Untersuchen nativer Binärdateien, von JavaScript- und Electron-Apps, .NET-Assemblies und Websites. Dieselben Werkzeuge kannst du auch im Terminal nutzen. Die Analyse läuft lokal; die Ergebnisse enthalten die Belege und Einschränkungen hinter jeder Schlussfolgerung.
|
||||
|
||||
Die Einrichtung registriert REA bei deinem Agenten und installiert passende Workflow-Anweisungen. Für native Analysen kannst du eine vorhandene Hopper- oder Ghidra-Installation verwenden. Mit deiner Zustimmung kann die Einrichtung auch Hopper installieren. Statische JavaScript-Analysen benötigen keines der beiden Programme.
|
||||
|
||||
> **[Besuche die REA-Website](https://rea.tools/)** für Einrichtungshinweise, bebilderte Anleitungen und echte Fallstudien.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
### Deinen Agenten einrichten
|
||||
|
||||
Wenn Node.js und npm installiert sind, führe diesen Befehl aus:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Wähle deine Agenten aus, prüfe die geplanten Änderungen und bestätige sie. Die Einrichtung fügt den MCP-Server von REA und passende Workflow-Anweisungen hinzu und sichert vorhandene Konfigurationen. Starte deinen Agenten anschließend neu.
|
||||
|
||||
Die Einrichtung unterstützt Claude Code, Codex, Cursor, Gemini CLI, Grok Build und [weitere Agenten](docs/installation.md#supported-agents). Informationen zur Provider-Konfiguration und manuellen MCP-Registrierung findest du unter [Installation und Einrichtung](docs/installation.md).
|
||||
|
||||
### Deinen Agenten fragen
|
||||
|
||||
```text
|
||||
Untersuche, wie die Suche in der Notes-App funktioniert, zeige mir die Belege und entwickle eine ähnliche Funktion für mein Projekt.
|
||||
```
|
||||
|
||||
Ersetze Notes durch deine Ziel-App und benenne die Funktion, die du verstehen möchtest.
|
||||
|
||||
### Das Terminal verwenden
|
||||
|
||||
Untersuche ein entpacktes JavaScript-/Electron-App-Verzeichnis oder eine ASAR-Datei:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Das Ergebnis enthält Module, Imports, Electron-Grenzen und die zugehörigen Belege. Ersetze den Pfad durch dein Ziel, beispielsweise `"D:/apps/example"` unter Windows.
|
||||
|
||||
Für die regelmäßige Nutzung kannst du den Befehl `rea` installieren:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Konfiguriere für native Analysen zuerst einen Provider. Native Befehle, Provider-Auswahl, Snapshots und Skriptnutzung beschreibt der [CLI- und Evidence-Leitfaden](docs/cli.md).
|
||||
|
||||
### REA aktualisieren
|
||||
|
||||
REA entwickelt sich schnell weiter; neue Versionen enthalten häufig Fehlerbehebungen. Halte deine Installation aktuell.
|
||||
|
||||
Für eine über npm installierte CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Um die Agentenregistrierungen und den Skill zu aktualisieren, führe den Einrichtungsbefehl aus, den das Update ausgibt.
|
||||
|
||||
Wenn du `npx` verwendest, aktualisiere die Agenteneinrichtung mit:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Prüfe die Änderungen und starte deinen Agenten neu. Für einzelne CLI-Aufrufe verwende `npx rea-agents@latest`, gefolgt vom gewünschten Befehl.
|
||||
|
||||
## So funktioniert REA
|
||||
|
||||
Dein Agent ruft REA über MCP auf, um das Ziel zu untersuchen und relevanten Code zu verfolgen. REA liefert Ergebnisse mit ihren Belegen zurück. Der Agent nutzt sie für weitere Fragen, Erklärungen oder zum Schreiben und Testen einer Implementierung. CLI-Befehle verwenden dieselben Workflows.
|
||||
|
||||

|
||||
|
||||
[Abbildung in voller Größe öffnen](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Was du analysieren kannst
|
||||
|
||||
REA benötigt Node.js 22.x (>=22.19), 24.x (>=24.11) oder 26+ sowie npm. Zusätzliche Werkzeuge und unterstützte Hostsysteme hängen vom Ziel ab:
|
||||
|
||||
| Ziel | Was REA zurückgibt | Voraussetzungen und Anleitung |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native Binärdateien | Pseudocode, Assemblercode, Zeichenketten, Symbole, Aufrufe und Verweise | Hopper, Ghidra oder IDA; [native Analyse](https://rea.tools/guides/native/) |
|
||||
| Offline-ELF-Struktur | Sektionen, Segmente, ursprüngliche Symbole/Relokationen und mögliche Schutzmaßnahmen aus statischer Analyse | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; [Binärdiagnostik](docs/binary-diagnostics.md) |
|
||||
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung; [Offline-EVM-Leitfaden](docs/evm-bytecode.md) |
|
||||
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools; optional GDB/pwndbg; [aufgezeichnete Abstürze](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Module, Imports, Source Maps, Routen, IPC und Beziehungen zu nativen Add-ons | Node.js und npm; [Anwendungsanalyse](https://rea.tools/guides/javascript/) |
|
||||
| Websites | Seitenstruktur, Skripte, Netzwerkbeobachtungen und angeforderte Screenshots | Ein Browser aus der Chrome-Familie; [Browseranalyse](https://rea.tools/guides/browser/) |
|
||||
| Gespeicherte Netzwerkaufzeichnungen | Anfragen, Antworten, zugängliche Nutzdaten und Quellpositionen | HAR; mitmdump unter Linux für Aufzeichnungen im nativen mitmproxy-Format; [Leitfaden zu Netzwerkaufzeichnungen](docs/web-network-captures.md) |
|
||||
| .NET-Assemblies | Metadaten, CIL-Anweisungen, deklarierte native Abhängigkeiten und Build-Vergleiche | Statische Untersuchung; [Leitfaden für verwalteten Code](docs/managed-code-analysis.md) |
|
||||
| Android-APKs | Manifest-Deklarationen, Klassen, dekompilierte Methoden und Verweise | JADX ohne grafische Oberfläche und ein vollständiges JDK unter Linux/macOS; [Android-Leitfaden](docs/android-analysis.md) |
|
||||
| Firmware | Bereiche, Extraktionsergebnisse und Übergaben an die native Analyse | Binwalk / Unblob unter Linux; [Firmware-Leitfaden](docs/firmware-analysis.md) |
|
||||
| Pakete und Ressourcen | Dateiinventare, Hashwerte, plists, Aufbau von Apple-Bundles und extrahierte Ressourcen | [Artefakt- und JavaScript-Leitfaden](docs/javascript-artifact-reconstruction.md), [Apple-Anwendungen](docs/apple-application-analysis.md) |
|
||||
| Prozessverhalten | Terminalausgabe, Interaktionen, Beobachtungen zu Prozessende und Dateisystem sowie Laufvergleiche | Linux/macOS mit nativer PTY; [Prozessaufzeichnung](docs/process-capture.md) |
|
||||
|
||||
Statische JavaScript- und .NET-Untersuchungen lesen die bereitgestellten Dateien, ohne die Anwendung auszuführen. Laufzeitaufzeichnungen führen das gewählte Ziel mit deinen Benutzerrechten aus oder interagieren damit; die jeweiligen Anleitungen beschreiben die Auswirkungen.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Native Formate und unterstützte Hostsysteme unterscheiden sich je nach Provider. Siehe [Hopper- und Ghidra-Einrichtung](docs/installation.md#hopper), den [IDA-Leitfaden](docs/ida-provider.md) und die [experimentelle Ghidra-Unterstützung unter Windows](docs/windows-ghidra-p0.md). Ghidra unterstützt auch [16-Bit-DOS-Analyse](docs/ghidra-dos.md). Die Provider-Auswahl beschreibt der [CLI-Leitfaden](docs/cli.md#choose-a-provider). Prüfe die [Verfügbarkeit in Releases](docs/installation.md#released-package-and-main) für Funktionen, die seit der neuesten npm-Version hinzugekommen sind.
|
||||
|
||||
## Fallstudien
|
||||
|
||||
### DX-Ball: Eine Stereo-Panning-Berechnung rekonstruieren
|
||||
|
||||
Verfolge einen Sound-Aufruf zur Hilfsfunktion, die Positionen in Panning-Werte umrechnet, untersuche die Anweisungen und überführe unvollständigen Pseudocode in C. Die Rekonstruktion besteht 3.205 Tests mit dem ursprünglichen x86-Code und reproduziert alle 63 Bytes der kompilierten Funktion.
|
||||
|
||||
[Fallstudie lesen](https://rea.tools/showcase/dx-ball/) · [Rekonstruktionsrepository](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: Die Electron-Zwischenablagebrücke verfolgen
|
||||
|
||||
Finde die Zwischenablage-API des Renderers, verfolge sie über preload und IPC bis zum Hauptprozess und untersuche das Zwischenablageformat samt Formatierungsdaten.
|
||||
|
||||
[Fallstudie lesen](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: Eine DOS-Geschossring-Berechnung wiederherstellen
|
||||
|
||||
Untersuche die 16-Bit-Anweisungen des ursprünglichen PC-98-Spiels, rekonstruiere die festen und zielgerichteten Winkelberechnungen und vergleiche das rekonstruierte C++ mit der Ausgabe des damaligen Compilers.
|
||||
|
||||
[Fallstudie lesen](https://rea.tools/showcase/th04/) · [Rekonstruktionsrepository](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Wenn du mit REA etwas Interessantes untersucht hast, würden wir es gern sehen. Teile deine Fallstudie in einem [Issue](https://github.com/morluto/rea/issues) oder [Pull Request](https://github.com/morluto/rea/pulls) und beschreibe das Ziel, deine Frage, wie REA geholfen hat und was du herausgefunden hast.
|
||||
|
||||
## Häufige Fragen
|
||||
|
||||
<details>
|
||||
<summary><strong>Welche Agenten können REA verwenden?</strong></summary>
|
||||
|
||||
Jeder Agent, der lokale MCP-Server unterstützt. Die Einrichtung konfiguriert die [unterstützten Agenten](docs/installation.md#supported-agents); andere Clients können die [manuelle MCP-Registrierung](docs/installation.md#mcp-registry) nutzen.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Brauche ich Hopper, Ghidra oder IDA?</strong></summary>
|
||||
|
||||
Für tiefgehende native Analysen wird eines dieser Programme verwendet. Statische JavaScript- und .NET-Untersuchungen funktionieren ohne native Analyse-Engine. Die Einrichtung kann Hopper nach Zustimmung installieren; Ghidra und IDA nutzen deine vorhandenen Installationen. Siehe [Provider-Einrichtung](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Muss ich Hopper vorher starten?</strong></summary>
|
||||
|
||||
REA startet Hopper, wenn eine Operation es benötigt. Unter macOS kann beim ersten Start ein Dialog erscheinen, in dem du den Demomodus auswählst oder deine Lizenz aktivierst. Siehe [Hopper-Start und Fehlerbehebung](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Was bewirkt die Installation des Skills über skills.sh?</strong></summary>
|
||||
|
||||
Der Skill stellt Untersuchungsanweisungen für deinen Agenten bereit. Verwende `rea setup`, um den MCP-Server von REA zu registrieren und passende Anweisungen zu installieren. Starte deinen Agenten anschließend neu. Siehe [Installation nur des Skills](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Welchen Code liefert REA zurück?</strong></summary>
|
||||
|
||||
Native Analysen liefern Pseudocode und Assemblercode. JavaScript-/Electron-Analysen rekonstruieren Module und ihre Beziehungen. Dein Agent nutzt diese Ergebnisse zum Schreiben und Testen einer Implementierung; die [Fallstudien](#fallstudien) zeigen konkrete Beispiele.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Lädt REA meine App hoch?</strong></summary>
|
||||
|
||||
REA analysiert Ziele lokal. Dein Agent erhält die Werkzeugergebnisse; sein Modellanbieter hat eine eigene Datenrichtlinie.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Was soll ich tun, wenn ich einen Fehler finde?</strong></summary>
|
||||
|
||||
Aktualisiere zuerst; eine neuere Version könnte den Fehler bereits behoben haben.
|
||||
|
||||
Für eine über npm installierte CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Für die Agenteneinrichtung über `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Wenn du einen Agenten verwendest, schließe die [Aktualisierung der Einrichtung](#rea-aktualisieren) ab und starte ihn neu. Wiederhole dieselbe Aufgabe. Besteht das Problem weiterhin, [öffne ein Issue](https://github.com/morluto/rea/issues) mit deiner REA-Version, dem Zieltyp, den Reproduktionsschritten und der Fehlerausgabe.
|
||||
|
||||
</details>
|
||||
|
||||
## Dokumentation
|
||||
|
||||
Beginne mit den [praxisnahen Anleitungen](https://rea.tools/guides/) auf der Website. Genaue Optionen, Voraussetzungen und Ergebniskontrakte findest du hier:
|
||||
|
||||
- [Installation und Einrichtung](docs/installation.md): Agentenregistrierung, Provider-Konfiguration, Updates und Deinstallation.
|
||||
- [Bereitschaft und Fehlerbehebung](docs/installation.md#check-readiness-for-your-task): Diagnose eines bestimmten Agenten oder einer Analyse-Engine.
|
||||
- [CLI und Evidence](docs/cli.md): Befehle, Provider-Auswahl, Snapshots, Import/Export und Exit-Status.
|
||||
- [MCP-Kontrakte](docs/mcp-contracts.md) und [Agenten-Prompts](docs/mcp-prompts.md): Werkzeugergebnisse, Sitzungen und geführte Untersuchungen.
|
||||
- [Werkzeugkatalog](docs/mcp-contracts.md#generated-catalog): Beim Build erzeugtes Inventar von Werkzeugen, Providern und CLI-Befehlen.
|
||||
- [Roadmap](docs/roadmap.md): Geplante Arbeiten und Fortschrittsübersichten zu Fähigkeiten.
|
||||
|
||||
Melde Sicherheitslücken gemäß [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Mitwirken
|
||||
|
||||
Wir freuen uns über deine Hilfe bei REA! [Öffne ein Issue](https://github.com/morluto/rea/issues), um einen Fehler zu melden oder eine Funktion vorzuschlagen, oder [sende einen Pull Request](https://github.com/morluto/rea/pulls), um Code oder Dokumentation zu verbessern.
|
||||
|
||||
Die Entwicklungsumgebung und Prüfungen beschreibt [CONTRIBUTING.md](CONTRIBUTING.md), die Verifikationspfade der [Testleitfaden](docs/testing.md) und die Projektstruktur die [Architekturübersicht](docs/architecture.mermaid).
|
||||
|
||||
## Projektlinks
|
||||
|
||||
[Website](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Sicherheit](SECURITY.md)
|
||||
|
||||
## Sternverlauf
|
||||
|
||||
🎉 **30.000 GitHub-Sterne – vielen Dank!**
|
||||
|
||||
Danke an alle, die REA nutzen, Fehler melden, Funktionswünsche äußern, Builds testen und Fehlerbehebungen beitragen.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="GitHub-Sternverlauf von REA" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Haftungsausschluss
|
||||
|
||||
REA stellt Werkzeuge für rechtmäßige Reverse-Engineering-Forschung, Analyse und Rekonstruktion bereit. Du bist dafür verantwortlich, erforderliche Genehmigungen einzuholen und geltendes Recht einzuhalten. Das Projekt unterstützt keine rechtswidrige oder unbefugte Nutzung.
|
||||
|
||||
## Lizenz
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · **Español** · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Ingeniería inversa de cualquier cosa
|
||||
|
||||
### Un solo MCP para la ingeniería inversa de binarios, aplicaciones y comportamiento en ejecución.
|
||||
|
||||
**¿Ves una función que te gusta? Entiende cómo funciona, hasta el nivel binario.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Sitio web](https://rea.tools/) · [Guías](https://rea.tools/guides/) · [Casos prácticos](https://rea.tools/showcase/)**
|
||||
|
||||
[Inicio rápido](#inicio-rápido) · [Cómo funciona REA](#cómo-funciona-rea) · [Qué puedes analizar](#qué-puedes-analizar) · [Casos prácticos](#casos-prácticos) · [Preguntas frecuentes](#preguntas-frecuentes) · [Documentación](#documentación)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA inicia su puente de análisis dentro de Hopper mientras inspecciona un binario nativo" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Comunidad REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Únete a la comunidad de ingeniería inversa</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Preguntas y respuestas · Comparte tus resultados</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
¿Has visto una función en una aplicación que quieres incorporar a tu producto? Pide a tu agente que la investigue con REA. Puede inspeccionar la aplicación sin su código fuente, explicar cómo funciona la función, mostrar las pruebas y crear una versión para tu proyecto.
|
||||
|
||||
REA conecta tu agente con herramientas para inspeccionar binarios nativos, aplicaciones JavaScript y Electron, ensamblados .NET y sitios web. También puedes usar las mismas herramientas desde la terminal. El análisis se ejecuta localmente y los resultados incluyen las pruebas y limitaciones que sustentan cada conclusión.
|
||||
|
||||
La configuración registra REA en tu agente e instala las instrucciones de trabajo correspondientes. El análisis nativo puede usar una instalación existente de Hopper o Ghidra; la configuración también puede instalar Hopper, si lo autorizas. El análisis estático de JavaScript no necesita ninguno de los dos motores.
|
||||
|
||||
> **[Visita el sitio web de REA](https://rea.tools/)** para encontrar instrucciones de configuración, guías ilustradas y casos reales.
|
||||
|
||||
## Inicio rápido
|
||||
|
||||
### Configura tu agente
|
||||
|
||||
Con Node.js y npm instalados, ejecuta:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Elige tus agentes, revisa los cambios propuestos y apruébalos. La configuración añade el servidor MCP de REA y las instrucciones de trabajo correspondientes, con copias de seguridad de la configuración existente. Reinicia tu agente al terminar.
|
||||
|
||||
La configuración admite Claude Code, Codex, Cursor, Gemini CLI, Grok Build y [otros agentes](docs/installation.md#supported-agents). Consulta [instalación y configuración](docs/installation.md) para configurar proveedores y registrar MCP manualmente.
|
||||
|
||||
### Pregunta a tu agente
|
||||
|
||||
```text
|
||||
Investiga cómo funciona la búsqueda en la aplicación Notes, muéstrame las pruebas y crea una función similar para mi proyecto.
|
||||
```
|
||||
|
||||
Sustituye Notes por la aplicación objetivo y especifica la función que quieres entender.
|
||||
|
||||
### Usa la terminal
|
||||
|
||||
Inspecciona un directorio extraído de una aplicación JavaScript/Electron o un archivo ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
El resultado incluye módulos, importaciones, límites de Electron y sus pruebas. Sustituye la ruta por la de tu objetivo, por ejemplo `"D:/apps/example"` en Windows.
|
||||
|
||||
Para instalar el comando `rea` y usarlo habitualmente:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Para el análisis nativo, configura primero un proveedor. Consulta la [guía de CLI y Evidence](docs/cli.md) para los comandos nativos, la selección de proveedores, las instantáneas y el uso en scripts.
|
||||
|
||||
### Actualiza REA
|
||||
|
||||
REA cambia rápidamente y las nuevas versiones incluyen correcciones frecuentes. Mantén tu instalación actualizada.
|
||||
|
||||
Para una CLI instalada mediante npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Para actualizar los registros de tus agentes y la skill, ejecuta el comando de configuración que muestra la actualización.
|
||||
|
||||
Si usas `npx`, actualiza la configuración de tu agente con:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Revisa los cambios de configuración y reinicia tu agente. Para ejecutar comandos de CLI puntuales, usa `npx rea-agents@latest` seguido del comando.
|
||||
|
||||
## Cómo funciona REA
|
||||
|
||||
Tu agente llama a REA mediante MCP para inspeccionar el objetivo y seguir el código relevante. REA devuelve los hallazgos junto con sus pruebas. El agente los utiliza para hacer preguntas de seguimiento, explicar el comportamiento o escribir y probar una implementación. Los comandos de CLI usan los mismos flujos de trabajo.
|
||||
|
||||

|
||||
|
||||
[Abre la figura a tamaño completo](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Qué puedes analizar
|
||||
|
||||
REA requiere Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, además de npm. Las herramientas adicionales y los sistemas anfitriones compatibles dependen del objetivo:
|
||||
|
||||
| Objetivo | Qué devuelve REA | Requisitos y guía |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Binarios nativos | Pseudocódigo, ensamblador, cadenas, símbolos, llamadas y referencias | Hopper, Ghidra o IDA; [análisis nativo](https://rea.tools/guides/native/) |
|
||||
| Estructura ELF sin ejecutar | Secciones, segmentos, símbolos/reubicaciones originales y posibles medidas de mitigación detectadas mediante análisis estático | pwntools proporcionado por quien llama, en Linux x64; [diagnóstico de binarios](docs/binary-diagnostics.md) |
|
||||
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal; [guía de EVM sin ejecutar](docs/evm-bytecode.md) |
|
||||
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama; GDB/pwndbg opcionales; [fallos registrados](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Módulos, importaciones, mapas de código fuente, rutas, IPC y relaciones con complementos nativos | Node.js y npm; [análisis de aplicaciones](https://rea.tools/guides/javascript/) |
|
||||
| Sitios web | Estructura de la página, scripts, observaciones de red y capturas de pantalla solicitadas | Un navegador de la familia Chrome; [análisis del navegador](https://rea.tools/guides/browser/) |
|
||||
| Capturas de red guardadas | Solicitudes, respuestas, contenido accesible de las cargas útiles y ubicaciones de origen | HAR; mitmdump en Linux para capturas en formato nativo de mitmproxy; [guía de capturas](docs/web-network-captures.md) |
|
||||
| Ensamblados .NET | Metadatos, instrucciones CIL, dependencias nativas declaradas y comparaciones de compilaciones | Inspección estática; [guía de código administrado](docs/managed-code-analysis.md) |
|
||||
| APK de Android | Declaraciones del manifiesto, clases, métodos descompilados y referencias | JADX sin interfaz gráfica y un JDK completo en Linux/macOS; [guía de Android](docs/android-analysis.md) |
|
||||
| Firmware | Regiones, resultados de extracción y derivaciones al análisis nativo | Binwalk / Unblob en Linux; [guía de firmware](docs/firmware-analysis.md) |
|
||||
| Paquetes y recursos | Inventarios de archivos, resúmenes criptográficos, plists, estructura de paquetes de Apple y recursos extraídos | [Guía de artefactos y JavaScript](docs/javascript-artifact-reconstruction.md), [aplicaciones de Apple](docs/apple-application-analysis.md) |
|
||||
| Comportamiento de procesos | Salida de la terminal, interacciones, observaciones de salida y del sistema de archivos, y comparaciones entre ejecuciones | Linux/macOS con una PTY nativa; [captura de procesos](docs/process-capture.md) |
|
||||
|
||||
La inspección estática de JavaScript y .NET lee los archivos suministrados sin ejecutar la aplicación. La captura en ejecución inicia el objetivo seleccionado o interactúa con él con los permisos de tu usuario; cada guía describe sus efectos.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Los formatos nativos y los sistemas anfitriones compatibles varían según el proveedor. Consulta [configuración de Hopper y Ghidra](docs/installation.md#hopper), la [guía de IDA](docs/ida-provider.md) y la [compatibilidad experimental de Ghidra en Windows](docs/windows-ghidra-p0.md). Ghidra también admite [análisis de DOS de 16 bits](docs/ghidra-dos.md). Para elegir un proveedor, consulta la [guía de CLI](docs/cli.md#choose-a-provider). Comprueba la [disponibilidad por versión](docs/installation.md#released-package-and-main) de las funciones añadidas después de la última versión publicada en npm.
|
||||
|
||||
## Casos prácticos
|
||||
|
||||
### DX-Ball: reconstruir un cálculo de panoramización de sonido
|
||||
|
||||
Sigue una llamada de sonido hasta la función auxiliar que convierte la posición en panoramización, inspecciona las instrucciones y transforma el pseudocódigo incompleto en C. La reconstrucción supera 3.205 casos con el x86 original y reproduce los 63 bytes de la función compilada.
|
||||
|
||||
[Lee el caso práctico](https://rea.tools/showcase/dx-ball/) · [Repositorio de reconstrucción](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: seguir el puente del portapapeles de Electron
|
||||
|
||||
Encuentra la API del portapapeles del proceso de renderizado, síguela a través de preload e IPC hasta el proceso principal e inspecciona el formato enriquecido del portapapeles.
|
||||
|
||||
[Lee el caso práctico](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: recuperar el cálculo de un anillo de proyectiles en DOS
|
||||
|
||||
Inspecciona las instrucciones de 16 bits del juego original para PC-98, recupera los cálculos de ángulos fijos y dirigidos, y compara el C++ reconstruido con la salida del compilador de la época.
|
||||
|
||||
[Lee el caso práctico](https://rea.tools/showcase/th04/) · [Repositorio de reconstrucción](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Si has usado REA para investigar algo interesante, nos gustaría verlo. Comparte tu caso en una [issue](https://github.com/morluto/rea/issues) o una [pull request](https://github.com/morluto/rea/pulls), incluyendo el objetivo, tu pregunta, cómo te ayudó REA y qué encontraste.
|
||||
|
||||
## Preguntas frecuentes
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Qué agentes pueden usar REA?</strong></summary>
|
||||
|
||||
Cualquier agente que admita servidores MCP locales. La configuración prepara los [agentes compatibles](docs/installation.md#supported-agents); otros clientes pueden usar el [registro manual de MCP](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Necesito Hopper, Ghidra o IDA?</strong></summary>
|
||||
|
||||
El análisis nativo en profundidad utiliza uno de ellos. La inspección estática de JavaScript y .NET funciona sin un motor de análisis nativo. La configuración puede instalar Hopper después de tu aprobación; Ghidra e IDA usan las instalaciones que ya tienes. Consulta [configuración de proveedores](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Tengo que iniciar Hopper primero?</strong></summary>
|
||||
|
||||
REA inicia Hopper cuando una operación lo necesita. En macOS, puede aparecer un diálogo al iniciarlo por primera vez para elegir el modo de demostración o activar tu licencia. Consulta [inicio de Hopper y resolución de problemas](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Qué hace la instalación de la skill desde skills.sh?</strong></summary>
|
||||
|
||||
La skill proporciona instrucciones de investigación a tu agente. Usa `rea setup` para registrar el servidor MCP de REA e instalar las instrucciones correspondientes; después reinicia tu agente. Consulta [instalación solo de la skill](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Qué código devuelve REA?</strong></summary>
|
||||
|
||||
El análisis nativo devuelve pseudocódigo y ensamblador. El análisis de JavaScript/Electron recupera módulos y sus relaciones. Tu agente utiliza estos hallazgos para escribir y probar una implementación; los [casos prácticos](#casos-prácticos) ofrecen ejemplos completos.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿REA sube mi aplicación?</strong></summary>
|
||||
|
||||
REA analiza los objetivos localmente. Tu agente recibe los resultados de las herramientas, y su proveedor de modelos tiene su propia política de datos.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>¿Qué debo hacer si encuentro un error?</strong></summary>
|
||||
|
||||
Actualiza primero; una versión reciente puede haber solucionado el problema.
|
||||
|
||||
Para una CLI instalada mediante npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Para configurar el agente mediante `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Si usas un agente, completa la [actualización de la configuración](#actualiza-rea) y reinícialo. Repite la misma tarea. Si el problema continúa, [abre una issue](https://github.com/morluto/rea/issues) con tu versión de REA, el tipo de objetivo, los pasos para reproducirlo y la salida del error.
|
||||
|
||||
</details>
|
||||
|
||||
## Documentación
|
||||
|
||||
Empieza con las [guías prácticas](https://rea.tools/guides/) del sitio web. Para opciones exactas, requisitos y contratos de resultados:
|
||||
|
||||
- [Instalación y configuración](docs/installation.md): registro de agentes, configuración de proveedores, actualizaciones y desinstalación.
|
||||
- [Preparación y resolución de problemas](docs/installation.md#check-readiness-for-your-task): diagnóstico de un agente o motor de análisis concreto.
|
||||
- [CLI y Evidence](docs/cli.md): comandos, selección de proveedores, instantáneas, importación/exportación y estados de salida.
|
||||
- [Contratos de MCP](docs/mcp-contracts.md) y [prompts para agentes](docs/mcp-prompts.md): resultados de herramientas, sesiones e investigaciones guiadas.
|
||||
- [Catálogo de herramientas](docs/mcp-contracts.md#generated-catalog): inventario de herramientas, proveedores y comandos de CLI generado durante la compilación.
|
||||
- [Hoja de ruta](docs/roadmap.md): trabajo previsto y seguimiento de capacidades.
|
||||
|
||||
Informa de vulnerabilidades siguiendo [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contribuir
|
||||
|
||||
¡Nos encantaría que ayudaras con REA! [Abre una issue](https://github.com/morluto/rea/issues) para comunicar un error o proponer una función, o [envía una pull request](https://github.com/morluto/rea/pulls) para mejorar el código o la documentación.
|
||||
|
||||
Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para preparar el entorno de desarrollo y las comprobaciones, la [guía de pruebas](docs/testing.md) para los procesos de verificación y el [mapa de arquitectura](docs/architecture.mermaid) para la estructura del proyecto.
|
||||
|
||||
## Enlaces del proyecto
|
||||
|
||||
[Sitio web](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Seguridad](SECURITY.md)
|
||||
|
||||
## Historial de estrellas
|
||||
|
||||
🎉 **30.000 estrellas en GitHub: ¡gracias!**
|
||||
|
||||
Gracias a quienes usan REA, informan de errores, solicitan funciones, prueban compilaciones y contribuyen con correcciones.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Historial de estrellas de REA en GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Aviso legal
|
||||
|
||||
REA proporciona herramientas para la investigación, el análisis y la reconstrucción mediante ingeniería inversa conforme a la ley. Eres responsable de obtener las autorizaciones necesarias y cumplir las leyes aplicables. El proyecto no respalda usos ilegales ni no autorizados.
|
||||
|
||||
## Licencia
|
||||
|
||||
[MIT](LICENSE)
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
<div align="center" dir="rtl">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی**
|
||||
|
||||
# REA: مهندسی معکوس هر چیزی
|
||||
|
||||
### یک MCP برای مهندسی معکوس فایلهای باینری، برنامهها و رفتار زمان اجرا.
|
||||
|
||||
**قابلیتی دیدهاید که دوستش دارید؟ سازوکارش را تا سطح باینری درک کنید.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55" /></a>
|
||||
|
||||
**[وبسایت](https://rea.tools/) · [راهنماها](https://rea.tools/guides/) · [نمونههای عملی](https://rea.tools/showcase/)**
|
||||
|
||||
[شروع سریع](#شروع-سریع) · [REA چگونه کار میکند؟](#rea-چگونه-کار-میکند) · [چه چیزهایی را میتوان تحلیل کرد؟](#چه-چیزهایی-را-میتوان-تحلیل-کرد) · [نمونههای عملی](#نمونههای-عملی) · [پرسشهای متداول](#پرسشهای-متداول) · [مستندات](#مستندات)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="اجرای پل تحلیل REA در Hopper هنگام بررسی یک فایل باینری بومی" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="انجمن REA"><tr><td align="center" width="360"><a href="https://discord.gg/GkcryMnJDM"><img src="docs/assets/discord.svg" height="42" alt="Discord" /><br /><strong>به انجمن مهندسی معکوس بپیوندید</strong></a><br /><sub>دیسکورد · پرسش و پاسخ · نمایش دستاوردها</sub></td></tr></table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<div dir="rtl">
|
||||
|
||||
قابلیتی در یک برنامه دیدهاید که میخواهید در محصول خودتان داشته باشید؟ از ایجنت خود بخواهید با REA آن را بررسی کند. ایجنت میتواند بدون دسترسی به کد منبع برنامه، نحوهٔ کار قابلیت را بررسی و توضیح دهد، شواهد را نشان دهد و نسخهای مشابه برای پروژهٔ شما بسازد.
|
||||
|
||||
REA ایجنت شما را به ابزارهایی برای بررسی باینریهای بومی، برنامههای JavaScript و Electron، اسمبلیهای .NET و وبسایتها متصل میکند. همین ابزارها از ترمینال نیز قابل استفادهاند. تحلیل بهصورت محلی انجام میشود و نتایج شامل شواهد و محدودیتهای پشت هر نتیجهگیری هستند.
|
||||
|
||||
فرایند راهاندازی، REA را در ایجنت شما ثبت میکند و دستورالعملهای گردشکار متناظر را نصب میکند. تحلیل بومی میتواند از نصب موجود Hopper یا Ghidra استفاده کند؛ همچنین راهاندازی میتواند با تأیید شما Hopper را بهصورت اختیاری نصب کند. تحلیل ایستای JavaScript به هیچیک از این موتورهای تحلیل نیاز ندارد.
|
||||
|
||||
> برای دستورالعملهای راهاندازی، راهنماهای تصویری و مطالعات موردی واقعی، **[به وبسایت REA مراجعه کنید](https://rea.tools/)**.
|
||||
|
||||
## شروع سریع
|
||||
|
||||
### ایجنت خود را راهاندازی کنید
|
||||
|
||||
پس از نصب Node.js و npm، دستور زیر را اجرا کنید:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
ایجنتهای موردنظر را انتخاب کنید، تغییرات پیشنهادی را بررسی و تأیید کنید. فرایند راهاندازی، سرور MCP مربوط به REA و دستورالعملهای گردشکار متناظر را اضافه میکند و از پیکربندی موجود نسخهٔ پشتیبان میگیرد. پس از آن ایجنت خود را مجدداً راهاندازی کنید.
|
||||
|
||||
راهاندازی از Claude Code، Codex، Cursor، Gemini CLI، Grok Build و [ایجنتهای دیگر](docs/installation.md#supported-agents) پشتیبانی میکند. برای پیکربندی ارائهدهنده و ثبت دستی MCP، [نصب و راهاندازی](docs/installation.md) را ببینید.
|
||||
|
||||
### از ایجنت خود بپرسید
|
||||
|
||||
```text
|
||||
بررسی کن جستوجو در برنامهٔ Notes چگونه کار میکند، شواهدش را نشان بده و قابلیتی مشابه برای پروژهٔ من بساز.
|
||||
```
|
||||
|
||||
بهجای Notes، نام برنامهٔ هدف و قابلیتی را قرار دهید که میخواهید درک کنید.
|
||||
|
||||
### استفاده از ترمینال
|
||||
|
||||
برای بررسی پوشهٔ استخراجشدهٔ یک برنامهٔ JavaScript/Electron یا فایل ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
نتیجه شامل ماژولها، importها، مرزهای Electron و شواهد مربوط به آنهاست. مسیر را با مسیر برنامهٔ هدف جایگزین کنید؛ مثلاً در ویندوز `"D:/apps/example"`.
|
||||
|
||||
برای نصب دستور `rea` جهت استفادهٔ مداوم:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
برای تحلیل بومی ابتدا یک ارائهدهنده پیکربندی کنید. برای دستورات بومی، انتخاب ارائهدهنده، snapshotها و اسکریپتنویسی، [راهنمای CLI و شواهد](docs/cli.md) را ببینید.
|
||||
|
||||
### بهروزرسانی REA
|
||||
|
||||
REA بهسرعت تغییر میکند و نسخههای جدید معمولاً شامل رفع اشکالهای متعددی هستند. نصب خود را بهروز نگه دارید.
|
||||
|
||||
برای CLI نصبشده از طریق npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
برای بهروزرسانی ثبت ایجنتها و skill، دستور setup نمایشدادهشده توسط بهروزرسانی را اجرا کنید.
|
||||
|
||||
اگر از `npx` استفاده میکنید، راهاندازی ایجنت را با دستور زیر بهروز کنید:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
تغییرات راهاندازی را بررسی کنید و ایجنت را دوباره اجرا کنید. برای اجرای یکبارهٔ دستورات CLI، از `npx rea-agents@latest` و سپس دستور موردنظر استفاده کنید.
|
||||
|
||||
## REA چگونه کار میکند؟
|
||||
|
||||
ایجنت شما از طریق MCP، REA را فراخوانی میکند تا هدف را بررسی و کد مرتبط را ردیابی کند. REA یافتهها را همراه با شواهد برمیگرداند. ایجنت از این نتایج برای طرح پرسشهای تکمیلی، توضیح رفتار یا نوشتن و آزمایش یک پیادهسازی استفاده میکند. دستورات CLI نیز از همین گردشکارها بهره میبرند.
|
||||
|
||||

|
||||
|
||||
[نمایش تصویر در اندازهٔ کامل](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## چه چیزهایی را میتوان تحلیل کرد؟
|
||||
|
||||
REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه npm نیاز دارد. ابزارهای اضافی و پشتیبانی سیستم میزبان به نوع هدف بستگی دارند:
|
||||
|
||||
| هدف | خروجی REA | پیشنیازها و راهنما |
|
||||
| --------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| باینریهای بومی | شبهکد، اسمبلی، رشتهها، نمادها، فراخوانیها و ارجاعات | Hopper، Ghidra یا IDA؛ [تحلیل بومی](https://rea.tools/guides/native/) |
|
||||
| ساختار ELF آفلاین | بخشها، سگمنتها، نمادها و relocationهای اصلی و گزینههای احتمالی کاهش آسیبپذیری ایستا | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ [تشخیص باینری](docs/binary-diagnostics.md) |
|
||||
| بایتکد EVM | selectorهای dispatch، offsetهای بایتی، آرگومانهای استنباطشده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی؛ [راهنمای EVM آفلاین](docs/evm-bytecode.md) |
|
||||
| کرشهای ثبتشدهٔ لینوکس | یادداشتهای خام، رجیسترها/سیگنالهای تمام threadهای ثبتشده و گزینههای احتمالی نگاشت | pwntools تأمینشده توسط فراخواننده؛ GDB/pwndbg اختیاری؛ [کرشهای ثبتشده](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | ماژولها، importها، source mapها، مسیرها، IPC و ارتباط افزونههای بومی | Node.js و npm؛ [تحلیل برنامه](https://rea.tools/guides/javascript/) |
|
||||
| وبسایتها | ساختار صفحه، اسکریپتها، مشاهدات شبکه و اسکرینشاتهای درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](https://rea.tools/guides/browser/) |
|
||||
| دادههای ضبطشدهٔ شبکه | درخواستها، پاسخها، payloadهای آشکارشده و مکان آنها در منبع | HAR؛ ابزار mitmdump در لینوکس برای ضبطهای بومی mitmproxy؛ [راهنمای ضبط شبکه](docs/web-network-captures.md) |
|
||||
| اسمبلیهای .NET | فراداده، دستورالعملهای CIL، وابستگیهای بومی اعلامشده و مقایسهٔ buildها | بررسی ایستا؛ [راهنمای کد مدیریتشده](docs/managed-code-analysis.md) |
|
||||
| فایلهای APK اندروید | تعاریف Manifest، کلاسها، متدهای دیکامپایلشده و ارجاعات | JADX بدون رابط گرافیکی و JDK کامل در Linux/macOS؛ [راهنمای اندروید](docs/android-analysis.md) |
|
||||
| Firmware | ناحیهها، نتایج استخراج و تحویل به ابزارهای تحلیل بومی | Binwalk / Unblob در لینوکس؛ [راهنمای Firmware](docs/firmware-analysis.md) |
|
||||
| بستهها و منابع | فهرست فایلها، digestها، فایلهای plist، ساختار bundle اپل و منابع استخراجشده | [راهنمای آرتیفکت و JavaScript](docs/javascript-artifact-reconstruction.md)، [برنامههای اپل](docs/apple-application-analysis.md) |
|
||||
| رفتار فرایند | خروجی ترمینال، تعاملات، وضعیت خروج، مشاهدات سیستم فایل و مقایسهٔ اجراها | Linux/macOS با PTY بومی؛ [ضبط فرایند](docs/process-capture.md) |
|
||||
|
||||
بررسی ایستای JavaScript و .NET فایلهای ارائهشده را بدون اجرای برنامه میخواند. ضبط زمان اجرا، هدف انتخابشده را با مجوزهای کاربری شما اجرا میکند یا با آن تعامل دارد؛ آثار این عملیات در راهنمای مربوط به هر نوع ضبط توضیح داده شدهاند.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
فرمتهای بومی و پشتیبانی سیستم میزبان بر اساس ارائهدهنده متفاوتاند. [راهاندازی Hopper و Ghidra](docs/installation.md#hopper)، [راهنمای IDA](docs/ida-provider.md) و [پشتیبانی آزمایشی Ghidra در ویندوز](docs/windows-ghidra-p0.md) را ببینید. Ghidra همچنین از [تحلیل DOS شانزدهبیتی](docs/ghidra-dos.md) پشتیبانی میکند. برای انتخاب ارائهدهنده، به [راهنمای CLI](docs/cli.md#choose-a-provider) مراجعه کنید. برای قابلیتهایی که پس از آخرین انتشار npm اضافه شدهاند، [وضعیت انتشار](docs/installation.md#released-package-and-main) را بررسی کنید.
|
||||
|
||||
## نمونههای عملی
|
||||
|
||||
### DX-Ball: بازسازی محاسبهٔ موقعیت استریوی صدا
|
||||
|
||||
یک فراخوانی صوتی را تا تابع کمکی تبدیل موقعیت به pan دنبال کنید، دستورالعملها را بررسی کنید و شبهکد ناقص را به C تبدیل کنید. بازسازی حاصل، هر ۳٬۲۰۵ مورد آزمایش x86 اصلی را با موفقیت پشت سر میگذارد و هر ۶۳ بایت تابع کامپایلشده را دقیقاً بازتولید میکند.
|
||||
|
||||
[مطالعهٔ موردی](https://rea.tools/showcase/dx-ball/) · [مخزن بازسازی](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: ردیابی پل کلیپبورد Electron
|
||||
|
||||
API کلیپبورد در renderer را پیدا کنید، مسیر آن را از preload و IPC تا فرایند اصلی دنبال کنید و قالب غنی کلیپبورد را بررسی کنید.
|
||||
|
||||
[مطالعهٔ موردی](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: بازیابی محاسبهٔ حلقهٔ گلوله در DOS
|
||||
|
||||
دستورالعملهای ۱۶بیتی بازی اصلی PC-98 را بررسی کنید، محاسبات زاویهٔ ثابت و هدفگیریشده را بازیابی کنید و کد C++ بازسازیشده را با خروجی کامپایلر تاریخی مقایسه کنید.
|
||||
|
||||
[مطالعهٔ موردی](https://rea.tools/showcase/th04/) · [مخزن بازسازی](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
اگر از REA برای بررسی موضوع جالبی استفاده کردهاید، خوشحال میشویم آن را به اشتراک بگذارید. یک [issue](https://github.com/morluto/rea/issues) یا [pull request](https://github.com/morluto/rea/pulls) ثبت کنید و هدف، پرسش خود، نقش REA و یافتههایتان را توضیح دهید.
|
||||
|
||||
## پرسشهای متداول
|
||||
|
||||
<details><summary><strong>کدام ایجنتها میتوانند از REA استفاده کنند؟</strong></summary>
|
||||
|
||||
هر ایجنتی که از سرورهای MCP محلی پشتیبانی کند. فرایند setup، [ایجنتهای پشتیبانیشده](docs/installation.md#supported-agents) را پیکربندی میکند؛ سایر کلاینتها میتوانند از [ثبت دستی MCP](docs/installation.md#mcp-registry) استفاده کنند.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>آیا به Hopper، Ghidra یا IDA نیاز دارم؟</strong></summary>
|
||||
|
||||
تحلیل عمیق بومی به یکی از آنها نیاز دارد. بررسی ایستای JavaScript و .NET بدون موتور تحلیل بومی کار میکند. setup میتواند با تأیید شما Hopper را نصب کند؛ Ghidra و IDA از نصبهای موجود شما استفاده میکنند. [راهاندازی ارائهدهنده](docs/installation.md#hopper) را ببینید.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>آیا باید ابتدا Hopper را اجرا کنم؟</strong></summary>
|
||||
|
||||
REA زمانی که عملیاتی به Hopper نیاز داشته باشد آن را اجرا میکند. در macOS، ممکن است هنگام نخستین اجرا پنجرهای برای انتخاب حالت آزمایشی یا فعالسازی مجوز نمایش داده شود. [راهاندازی Hopper و رفع اشکال](docs/installation.md#launcher-paths-and-troubleshooting) را ببینید.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>نصب skill از skills.sh چه کاری انجام میدهد؟</strong></summary>
|
||||
|
||||
این skill دستورالعملهای بررسی را برای ایجنت فراهم میکند. برای ثبت سرور MCP مربوط به REA و نصب دستورالعملهای متناظر، `rea setup` را اجرا کنید و سپس ایجنت را مجدداً راهاندازی کنید. [نصب مستقل skill](docs/installation.md#skill-only-installation) را ببینید.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA چه نوع کدی برمیگرداند؟</strong></summary>
|
||||
|
||||
تحلیل بومی، شبهکد و اسمبلی برمیگرداند. تحلیل JavaScript/Electron ماژولها و روابط میان آنها را بازیابی میکند. ایجنت شما از این یافتهها برای نوشتن و آزمایش یک پیادهسازی استفاده میکند؛ [نمونههای عملی](#نمونههای-عملی) مثالهای گامبهگام ارائه میدهند.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>آیا REA برنامهٔ من را آپلود میکند؟</strong></summary>
|
||||
|
||||
REA اهداف را بهصورت محلی تحلیل میکند. ایجنت شما نتایج ابزارها را دریافت میکند و ارائهدهندهٔ مدل آن سیاست مستقل خود را دربارهٔ دادهها دارد.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>اگر با خطا مواجه شدم چه کنم؟</strong></summary>
|
||||
|
||||
ابتدا بهروزرسانی کنید؛ ممکن است مشکل در نسخهای جدید رفع شده باشد.
|
||||
|
||||
برای CLI نصبشده با npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
برای راهاندازی ایجنت با `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
اگر از ایجنت استفاده میکنید، [بهروزرسانی راهاندازی](#بهروزرسانی-rea) را تکمیل و ایجنت را مجدداً اجرا کنید. همان کار را دوباره امتحان کنید. اگر مشکل ادامه داشت، یک [issue ثبت کنید](https://github.com/morluto/rea/issues) و نسخهٔ REA، نوع هدف، مراحل بازتولید و خروجی خطا را ذکر کنید.
|
||||
|
||||
</details>
|
||||
|
||||
## مستندات
|
||||
|
||||
از [راهنماهای عملی وبسایت](https://rea.tools/guides/) شروع کنید. برای گزینههای دقیق، پیشنیازها و قراردادهای خروجی:
|
||||
|
||||
- [نصب و راهاندازی](docs/installation.md): ثبت ایجنت، پیکربندی ارائهدهنده، بهروزرسانی و حذف نصب.
|
||||
- [آمادگی و رفع اشکال](docs/installation.md#check-readiness-for-your-task): عیبیابی یک ایجنت یا موتور تحلیل.
|
||||
- [CLI و شواهد](docs/cli.md): دستورات، انتخاب ارائهدهنده، snapshotها، ورود/خروج داده و وضعیتهای خروج.
|
||||
- [قراردادهای MCP](docs/mcp-contracts.md) و [پرامپتهای ایجنت](docs/mcp-prompts.md): نتایج ابزارها، نشستها و بررسیهای هدایتشده.
|
||||
- [فهرست ابزارها](docs/mcp-contracts.md#generated-catalog): فهرست تولیدشده هنگام build از ابزارها، ارائهدهندگان و دستورات CLI.
|
||||
- [نقشهٔ راه](docs/roadmap.md): برنامههای آینده و پیگیری قابلیتها.
|
||||
|
||||
آسیبپذیریها را از طریق [SECURITY.md](SECURITY.md) گزارش کنید.
|
||||
|
||||
## مشارکت
|
||||
|
||||
از کمک شما به REA استقبال میکنیم! برای گزارش خطا یا پیشنهاد قابلیت، یک [issue باز کنید](https://github.com/morluto/rea/issues) یا برای بهبود کد و مستندات [pull request بفرستید](https://github.com/morluto/rea/pulls).
|
||||
|
||||
برای راهاندازی محیط توسعه و بررسیها، [CONTRIBUTING.md](CONTRIBUTING.md)، برای مراحل اعتبارسنجی [آزمایشها](docs/testing.md) و برای ساختار پروژه [نقشهٔ معماری](docs/architecture.mermaid) را ببینید.
|
||||
|
||||
## لینکهای پروژه
|
||||
|
||||
[وبسایت](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [امنیت](SECURITY.md)
|
||||
|
||||
## تاریخچهٔ ستارهها
|
||||
|
||||
🎉 **۳۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
|
||||
|
||||
از همهٔ کسانی که از REA استفاده میکنند، خطاها را گزارش میدهند، قابلیتهای جدید پیشنهاد میکنند، buildها را آزمایش میکنند و در بهبود پروژه مشارکت دارند سپاسگزاریم.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date"><picture><source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" /><source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" /><img alt="تاریخچهٔ ستارههای GitHub پروژهٔ REA" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" /></picture></a>
|
||||
|
||||
## سلب مسئولیت
|
||||
|
||||
REA ابزارهایی برای پژوهش، تحلیل و بازسازی قانونی در حوزهٔ مهندسی معکوس فراهم میکند. مسئولیت دریافت مجوزهای لازم و رعایت قوانین مربوط بر عهدهٔ شماست. این پروژه استفادهٔ غیرقانونی یا بدون مجوز را تأیید نمیکند.
|
||||
|
||||
## مجوز
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
</div>
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · **Français** · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA : rétro-ingénierie de tout
|
||||
|
||||
### Un seul MCP pour la rétro-ingénierie des binaires, des applications et du comportement à l'exécution.
|
||||
|
||||
**Repérez une fonctionnalité qui vous plaît. Comprenez son fonctionnement, jusqu'au niveau du binaire.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Site web](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Études de cas](https://rea.tools/showcase/)**
|
||||
|
||||
[Démarrage rapide](#démarrage-rapide) · [Comment fonctionne REA](#comment-fonctionne-rea) · [Ce que vous pouvez analyser](#ce-que-vous-pouvez-analyser) · [Études de cas](#études-de-cas) · [Questions fréquentes](#questions-fréquentes) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA lance son pont d'analyse dans Hopper pendant l'inspection d'un binaire natif" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Rejoignez la communauté de rétro-ingénierie</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Questions et réponses · Partage de projets</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Vous avez repéré dans une application une fonctionnalité que vous aimeriez avoir dans votre propre produit ? Demandez à votre agent de l'étudier avec REA. Il peut inspecter l'application sans son code source, expliquer le fonctionnement de la fonctionnalité, présenter les preuves et en construire une version pour votre projet.
|
||||
|
||||
REA connecte votre agent à des outils d'inspection de binaires natifs, d'applications JavaScript et Electron, d'assemblys .NET et de sites web. Les mêmes outils sont aussi disponibles depuis votre terminal. L'analyse s'exécute localement, et chaque conclusion s'accompagne des preuves et des limites sur lesquelles elle repose.
|
||||
|
||||
Setup enregistre REA auprès de votre agent et installe les instructions de workflow correspondantes. L'analyse native peut utiliser une installation existante de Hopper ou de Ghidra ; Setup peut aussi installer Hopper après votre accord. L'analyse statique JavaScript ne nécessite aucun de ces moteurs.
|
||||
|
||||
> **[Visitez le site web de REA](https://rea.tools/)** pour les instructions d'installation, des guides illustrés et des études de cas réelles.
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
### Configurer votre agent
|
||||
|
||||
Une fois Node.js et npm installés, exécutez :
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Choisissez vos agents, vérifiez les modifications proposées et approuvez-les. Setup ajoute le serveur MCP de REA et les instructions de workflow correspondantes, et sauvegarde la configuration existante. Redémarrez ensuite votre agent.
|
||||
|
||||
Setup prend en charge Claude Code, Codex, Cursor, Gemini CLI, Grok Build et [d'autres agents](docs/installation.md#supported-agents). Consultez [Installation et configuration](docs/installation.md) pour la configuration des fournisseurs et l'enregistrement MCP manuel.
|
||||
|
||||
### Interroger votre agent
|
||||
|
||||
```text
|
||||
Comprends comment fonctionne la recherche dans l'app Notes, montre-moi les preuves
|
||||
et construis une fonctionnalité similaire pour mon projet.
|
||||
```
|
||||
|
||||
Remplacez Notes par votre application cible et la fonctionnalité que vous voulez comprendre.
|
||||
|
||||
### Utiliser le terminal
|
||||
|
||||
Inspectez le répertoire extrait ou l'archive ASAR d'une application JavaScript/Electron :
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Le résultat comprend les modules, les imports, les frontières Electron et les preuves associées. Remplacez le chemin par votre cible, par exemple `"D:/apps/example"` sous Windows.
|
||||
|
||||
Pour installer la commande `rea` pour un usage régulier :
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Pour l'analyse native, configurez d'abord un fournisseur. Consultez le [guide CLI et Evidence](docs/cli.md) pour les commandes natives, le choix du fournisseur, les snapshots et l'utilisation dans des scripts.
|
||||
|
||||
### Mettre à jour REA
|
||||
|
||||
REA évolue rapidement, et les nouvelles versions apportent fréquemment des corrections de bugs. Maintenez votre installation à jour.
|
||||
|
||||
Pour une CLI installée avec npm :
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Pour actualiser les enregistrements de vos agents et le skill, exécutez la commande setup affichée par la mise à jour.
|
||||
|
||||
Si vous utilisez `npx`, mettez à jour la configuration de votre agent avec :
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Vérifiez les modifications proposées par Setup et redémarrez votre agent. Pour des commandes CLI ponctuelles, utilisez `npx rea-agents@latest` suivi de la commande.
|
||||
|
||||
## Comment fonctionne REA
|
||||
|
||||
Votre agent appelle REA via MCP pour inspecter la cible et suivre le code pertinent. REA renvoie ses constatations avec les preuves correspondantes. L'agent s'en sert pour poser des questions complémentaires, expliquer le comportement, ou écrire et tester une implémentation. Les commandes CLI utilisent les mêmes workflows.
|
||||
|
||||

|
||||
|
||||
[Ouvrir la figure en taille réelle](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Ce que vous pouvez analyser
|
||||
|
||||
REA nécessite Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, ainsi que npm. Les outils supplémentaires et les hôtes pris en charge dépendent de la cible :
|
||||
|
||||
| Cible | Ce que REA renvoie | Prérequis et guide |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binaires natifs | Pseudo-code, assembleur, chaînes, symboles, appels et références | Hopper, Ghidra ou IDA ; [analyse native](https://rea.tools/guides/native/) |
|
||||
| Structure ELF hors ligne | Sections, segments, symboles et relocations d'origine, et protections statiques candidates | pwntools fourni par l'utilisateur sous Linux x64 ; [diagnostics binaires](docs/binary-diagnostics.md) |
|
||||
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal ; [guide EVM hors ligne](docs/evm-bytecode.md) |
|
||||
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur ; GDB/pwndbg en option ; [crashs enregistrés](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC et relations avec les modules natifs | Node.js et npm ; [analyse d'applications](https://rea.tools/guides/javascript/) |
|
||||
| Sites web | Structure des pages, scripts, observations réseau et captures d'écran demandées | Un navigateur de la famille Chrome ; [analyse du navigateur](https://rea.tools/guides/browser/) |
|
||||
| Captures réseau enregistrées | Requêtes, réponses, payloads exposés et emplacements dans le code source | HAR ; mitmdump sous Linux pour les captures mitmproxy natives ; [guide des captures](docs/web-network-captures.md) |
|
||||
| Assemblys .NET | Métadonnées, instructions CIL, dépendances natives déclarées et comparaisons de builds | Inspection statique ; [guide du code managé](docs/managed-code-analysis.md) |
|
||||
| APK Android | Déclarations du manifeste, classes, méthodes décompilées et références | JADX headless et un JDK complet sous Linux/macOS ; [guide Android](docs/android-analysis.md) |
|
||||
| Firmware | Régions, résultats d'extraction et passage de relais vers l'analyse native | Binwalk / Unblob sous Linux ; [guide firmware](docs/firmware-analysis.md) |
|
||||
| Paquets et ressources | Inventaires de fichiers, empreintes, plists, anatomie des bundles Apple et ressources extraites | [Guide artefacts et JavaScript](docs/javascript-artifact-reconstruction.md), [applications Apple](docs/apple-application-analysis.md) |
|
||||
| Comportement des processus | Sortie du terminal, interactions, code de sortie, observations du système de fichiers et comparaisons | Linux/macOS avec un PTY natif ; [capture de processus](docs/process-capture.md) |
|
||||
|
||||
L'inspection statique JavaScript et .NET lit les fichiers fournis sans exécuter l'application. La capture à l'exécution lance la cible choisie ou interagit avec elle avec vos permissions utilisateur ; chaque guide d'exécution décrit ses effets.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Les formats natifs et les hôtes pris en charge varient selon le fournisseur. Consultez [la configuration de Hopper et Ghidra](docs/installation.md#hopper), le [guide IDA](docs/ida-provider.md) et [la prise en charge expérimentale de Ghidra sous Windows](docs/windows-ghidra-p0.md). Ghidra prend aussi en charge [l'analyse DOS 16 bits](docs/ghidra-dos.md). Pour les gros binaires, augmentez son délai de démarrage avec `REA_GHIDRA_STARTUP_TIMEOUT_MS`. Pour le choix du fournisseur, consultez le [guide CLI](docs/cli.md#choose-a-provider). Vérifiez la [disponibilité dans les versions publiées](docs/installation.md#released-package-and-main) pour les fonctionnalités ajoutées depuis la dernière version npm.
|
||||
|
||||
## Études de cas
|
||||
|
||||
### DX-Ball : reconstruire un calcul de panoramique sonore
|
||||
|
||||
Suivez un appel sonore jusqu'à la fonction qui convertit une position en panoramique, inspectez ses instructions et transformez un pseudo-code incomplet en C. La reconstruction réussit 3 205 cas issus du x86 d'origine et reproduit à l'identique les 63 octets de la fonction compilée.
|
||||
|
||||
[Lire l'étude de cas](https://rea.tools/showcase/dx-ball/) · [Dépôt de la reconstruction](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion : suivre le pont du presse-papiers d'Electron
|
||||
|
||||
Trouvez l'API de presse-papiers du renderer, suivez-la à travers le preload et l'IPC jusqu'au processus principal, puis inspectez le format de presse-papiers enrichi.
|
||||
|
||||
[Lire l'étude de cas](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04 : retrouver un calcul d'anneau de projectiles sous DOS
|
||||
|
||||
Inspectez les instructions 16 bits du jeu PC-98 d'origine, retrouvez les calculs d'angles fixes et visés, et comparez le C++ reconstruit avec la sortie du compilateur d'époque.
|
||||
|
||||
[Lire l'étude de cas](https://rea.tools/showcase/th04/) · [Dépôt de la reconstruction](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Si vous avez utilisé REA sur un projet intéressant, nous serions ravis de le découvrir. Partagez votre cas dans une [issue](https://github.com/morluto/rea/issues) ou une [pull request](https://github.com/morluto/rea/pulls), en précisant la cible, votre question, la manière dont REA vous a aidé et ce que vous avez découvert.
|
||||
|
||||
## Questions fréquentes
|
||||
|
||||
<details>
|
||||
<summary><strong>Quels agents peuvent utiliser REA ?</strong></summary>
|
||||
|
||||
Tout agent compatible avec les serveurs MCP locaux. Setup configure les [agents pris en charge](docs/installation.md#supported-agents) ; les autres clients peuvent utiliser [l'enregistrement MCP manuel](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Ai-je besoin de Hopper, Ghidra ou IDA ?</strong></summary>
|
||||
|
||||
L'analyse native approfondie utilise l'un d'eux. L'inspection statique JavaScript et .NET fonctionne sans moteur d'analyse native. Setup peut installer Hopper après votre accord ; Ghidra et IDA utilisent vos installations existantes. Consultez [la configuration des fournisseurs](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Dois-je lancer Hopper au préalable ?</strong></summary>
|
||||
|
||||
REA lance Hopper lorsqu'une opération en a besoin. Sous macOS, une boîte de dialogue de premier lancement peut vous demander de choisir le mode démo ou d'activer votre licence. Consultez [Démarrage et dépannage de Hopper](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Que fait l'installation du skill depuis skills.sh ?</strong></summary>
|
||||
|
||||
Le skill fournit des instructions d'investigation à votre agent. Utilisez `rea setup` pour enregistrer le serveur MCP de REA et installer les instructions correspondantes, puis redémarrez votre agent. Consultez [l'installation du skill seul](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Quel code REA renvoie-t-il ?</strong></summary>
|
||||
|
||||
L'analyse native renvoie du pseudo-code et de l'assembleur. L'analyse JavaScript/Electron récupère les modules et leurs relations. Votre agent s'appuie sur ces résultats pour écrire et tester une implémentation ; les [études de cas](#études-de-cas) en donnent des exemples concrets.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA envoie-t-il mon application en ligne ?</strong></summary>
|
||||
|
||||
REA analyse les cibles localement. Votre agent reçoit les résultats des outils, et son fournisseur de modèle applique sa propre politique de données.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Que faire si je rencontre un bug ?</strong></summary>
|
||||
|
||||
Commencez par mettre à jour ; une version récente l'a peut-être déjà corrigé.
|
||||
|
||||
Pour une CLI installée avec npm :
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Pour une configuration d'agent via `npx` :
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Si vous utilisez un agent, terminez l'[actualisation de la configuration](#mettre-à-jour-rea) et redémarrez-le. Relancez la même tâche. Si le problème persiste, [ouvrez une issue](https://github.com/morluto/rea/issues) en indiquant votre version de REA, le type de cible, les étapes pour reproduire le problème et la sortie d'erreur.
|
||||
|
||||
</details>
|
||||
|
||||
## Documentation
|
||||
|
||||
Commencez par les [guides pas à pas](https://rea.tools/guides/) du site web. Pour les options exactes, les prérequis et les contrats de résultats :
|
||||
|
||||
- [Installation et configuration](docs/installation.md) : enregistrement des agents, configuration des fournisseurs, mises à jour et désinstallation.
|
||||
- [Vérification et dépannage](docs/installation.md#check-readiness-for-your-task) : diagnostiquer un agent ou un moteur d'analyse précis.
|
||||
- [CLI et Evidence](docs/cli.md) : commandes, choix du fournisseur, snapshots, import/export et codes de sortie.
|
||||
- [Contrats MCP](docs/mcp-contracts.md) et [prompts pour agents](docs/mcp-prompts.md) : résultats des outils, sessions et investigations guidées.
|
||||
- [Catalogue des outils](docs/mcp-contracts.md#generated-catalog) : inventaire des outils, fournisseurs et commandes CLI, généré lors du build.
|
||||
- [Feuille de route](docs/roadmap.md) : travaux prévus et suivi des capacités.
|
||||
|
||||
Signalez les vulnérabilités via [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contribuer
|
||||
|
||||
Votre aide sur REA est la bienvenue ! [Ouvrez une issue](https://github.com/morluto/rea/issues) pour signaler un bug ou proposer une fonctionnalité, ou [envoyez une pull request](https://github.com/morluto/rea/pulls) pour améliorer le code ou la documentation.
|
||||
|
||||
Consultez [CONTRIBUTING.md](CONTRIBUTING.md) pour l'environnement de développement et les vérifications, la page [tests](docs/testing.md) pour les filières de vérification, et la [carte de l'architecture](docs/architecture.mermaid) pour la structure du projet.
|
||||
|
||||
## Liens du projet
|
||||
|
||||
[Site web](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Sécurité](SECURITY.md)
|
||||
|
||||
## Historique des étoiles
|
||||
|
||||
🎉 **30 000 étoiles GitHub — merci !**
|
||||
|
||||
Merci à toutes les personnes qui utilisent REA, signalent des bugs, proposent des fonctionnalités, testent les builds et contribuent des correctifs.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Historique des étoiles GitHub de REA" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Avertissement
|
||||
|
||||
REA fournit des outils destinés à la recherche, à l'analyse et à la reconstruction légales en rétro-ingénierie. Il vous appartient d'obtenir toute autorisation nécessaire et de respecter les lois applicables. Le projet n'encourage aucune utilisation illégale ou non autorisée.
|
||||
|
||||
## Licence
|
||||
|
||||
[MIT](LICENSE)
|
||||
+197
-214
@@ -1,23 +1,36 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · **日本語** · [한국어](README_ko.md) · [العربية](README_ar.md)
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · **日本語** · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA:あらゆるものをリバースエンジニアリング
|
||||
|
||||
### エージェントでアプリの動作からネイティブバイナリまで調べる。
|
||||
### バイナリ、アプリケーション、実行時の動作を、ひとつの MCP でリバースエンジニアリング。
|
||||
|
||||
**気になる機能を見つけ、仕組みを理解し、自分の形で実装する。**
|
||||
**気に入った機能を見つけたら、その仕組みをバイナリのレベルまで理解する。**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#調査ツールカタログ)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[クイックスタート](#クイックスタート) · [現在の対応状況](#現在の対応状況) · [バイナリから動作へ](#バイナリから動作へ) · [調査ツールカタログ](#調査ツールカタログ) · [ロードマップ](#ロードマップ) · [仕組み](#仕組み)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
<table aria-label="REA community">
|
||||
**[ウェブサイト](https://rea.tools/) · [ガイド](https://rea.tools/guides/) · [事例](https://rea.tools/showcase/)**
|
||||
|
||||
[クイックスタート](#クイックスタート) · [REA の仕組み](#rea-の仕組み) · [分析できる対象](#分析できる対象) · [事例](#事例) · [よくある質問](#よくある質問) · [ドキュメント](#ドキュメント)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA が Hopper 内で分析ブリッジを起動し、ネイティブバイナリを調べる様子" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA コミュニティ">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
@@ -31,273 +44,243 @@
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper 内で解析ブリッジを起動し、ネイティブバイナリを調べる REA" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
アプリの機能を自分のプロダクトにも取り入れたいときは、REA を使ってエージェントに調査を依頼できます。ソースコードがなくても、アプリを調べ、仕組みと根拠を示し、あなたのプロジェクト向けに同様の機能を実装できます。
|
||||
アプリに、自分の製品にも取り入れたい機能を見つけましたか?エージェントに REA で調査を依頼してください。ソースコードがなくてもアプリを調べ、機能の仕組みを説明し、根拠を示したうえで、あなたのプロジェクト向けに実装できます。
|
||||
|
||||
REA はネイティブバイナリ、JavaScript/Electron アプリ、.NET アセンブリ、Web サイトの解析ツールを提供します。エージェントからもターミナルからも使えます。解析はローカルで実行され、結果には根拠と制限が含まれます。
|
||||
REA はエージェントを分析ツールにつなぎ、ネイティブバイナリ、JavaScript や Electron のアプリ、.NET アセンブリ、ウェブサイトを調べられるようにします。同じツールをターミナルからも使えます。分析はローカルで実行され、結果には各結論を裏付ける根拠と制限事項が含まれます。
|
||||
|
||||
Setup はエージェントを設定し、既存の Hopper または Ghidra に接続します。承認後に Hopper をインストールすることもできます。
|
||||
セットアップはエージェントに REA を登録し、対応するワークフローの手順をインストールします。ネイティブ分析には既存の Hopper または Ghidra を使えます。承認すれば、セットアップで Hopper をインストールすることもできます。静的な JavaScript 分析には、どちらのエンジンも必要ありません。
|
||||
|
||||
## エージェントに頼むだけ
|
||||
|
||||
[セットアップ](#クイックスタート)後にエージェントを再起動し、次のように依頼します:
|
||||
|
||||
```text
|
||||
メモアプリの検索機能を調べて根拠を示し、私のプロジェクト向けに同様の機能を実装してください。
|
||||
```
|
||||
|
||||
メモアプリを調べたいアプリに置き換えるか、まず概要を依頼してください。
|
||||
|
||||
## バイナリから動作へ
|
||||
|
||||
| 逆コンパイル | 理解 | 再現 |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| ネイティブアプリや実行ファイルから、プロシージャ、疑似コード、アセンブリ、文字列、シンボル、セグメント、メタデータを復元します。 | 呼び出し元、呼び出し先、クロスリファレンス、コールグラフをたどり、機能やアルゴリズムの実際の動作を説明できる状態にします。 | エージェントが得た知見を、あなたの技術スタック、画面、要件に合うプロダクト機能へ変えます。 |
|
||||
|
||||
REA は調査をバイナリ上の根拠に結び付けます。元のソースコードを復元したり、アプリ全体を自動複製したりするとは主張しません。
|
||||
|
||||
## REA を選ぶ理由
|
||||
|
||||
| | |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **エージェント向け** | コンパイル済みアプリについて質問し、推測ではなく根拠を集めさせることができます。 |
|
||||
| **CLI と MCP** | ターミナルとコーディングエージェントから同じリバースエンジニアリング機能を使えます。 |
|
||||
| **複雑さを処理** | ツール設定、アプリの読み込み、調査の維持、終了後のクリーンアップを REA が担います。 |
|
||||
| **一連の調査に対応** | 最初の概要から疑似コード、呼び出し関係、型、実装の手掛かりまで掘り下げられます。 |
|
||||
| **ローカルで解析** | 解析は対応するローカルホストで実行され、REA がバイナリをホスト型解析サービスへ送ることはありません。 |
|
||||
| **コンテキストを維持** | 質問ごとに解析を最初からやり直さず、複数のバイナリを続けて調査できます。 |
|
||||
> **[REA のウェブサイト](https://rea.tools/)**で、セットアップ手順、図解ガイド、実際の事例を確認できます。
|
||||
|
||||
## クイックスタート
|
||||
|
||||
### セットアップを実行(推奨)
|
||||
### エージェントを設定する
|
||||
|
||||
REA をエージェントで使うための設定を行います:
|
||||
Node.js と npm がインストールされている環境で実行します。
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Setup は最初に連携するエージェントを複数選択できるようにします。既存の REA 登録は初期選択されますが、検出されただけのクライアントは自動選択されず、未設定のクライアントも選べます。具体的なパスと変更内容を確認してから承認してください。選択したエージェントには通常 REA のワークフローをインストールします。Hopper は別の任意操作で、個別の承認が必要です。既存の Ghidra のパスも登録できます。
|
||||
使うエージェントを選び、予定されている変更を確認して承認してください。セットアップは REA の MCP サーバーと対応するワークフローの手順を追加し、既存の設定をバックアップします。完了後にエージェントを再起動してください。
|
||||
|
||||
変更は事前に表示され、既存の設定はバックアップされます。要件と追加オプションは[インストールガイド](docs/installation.md)を参照してください。
|
||||
セットアップは Claude Code、Codex、Cursor、Gemini CLI、Grok Build、および[その他のエージェント](docs/installation.md#supported-agents)に対応しています。プロバイダーの設定と MCP の手動登録については、[インストールとセットアップ](docs/installation.md)を参照してください。
|
||||
|
||||
### エージェントで使う
|
||||
|
||||
設定後にエージェントを再起動し、調べたいアプリや機能を説明します。REA は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、VS Code に対応します。既存の REA 登録は初期選択され、それ以外の検出済みクライアントは選択が必要です。その他のエージェントは下記の MCP 設定を使えます。
|
||||
|
||||
Hopper はデモモードで使えます。初回起動の画面ではデモを選択するか、既存のライセンスを入力してください。
|
||||
|
||||
### ターミナルで使う
|
||||
|
||||
設定後に実行します:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
### rea コマンドをインストール
|
||||
|
||||
コマンドラインツールをインストールします:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js と npm を先にインストールしてください。ターミナルで実行すると、インストーラーは `rea` を追加してセットアップを開始します。
|
||||
|
||||
npm でインストールし、セットアップを実行することもできます:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 要件
|
||||
|
||||
- macOS 12 以降
|
||||
- Ubuntu 24.04+、Fedora 41+、または 64 ビット Arch Linux
|
||||
- Node.js 22.19+ または 24.11+ と npm
|
||||
|
||||
ネイティブバイナリ解析には Hopper または Ghidra が必要です。Hopper は別製品です。デモにはベンダー所定の制限がありますが、有料ライセンスは必須ではありません。
|
||||
|
||||
Ghidra は Linux x64 と macOS x64/arm64 に対応します。Ghidra 12.1.4 と完全な 64 ビット JDK 21 を別途インストールし、REA で使うように設定してください。macOS ではホストのアーキテクチャに合うネイティブデコンパイラーも必要です。
|
||||
|
||||
Setup はインストールを確認し、パスを保存できます。Ghidra、Java、Node.js、npm、Homebrew のインストールや更新は行いません。
|
||||
|
||||
Windows の Ghidra 解析は利用できません。プロセスの所有権、プライベートディレクトリの権限、安全なパス検証が未実装のため、Ghidra と Java を正しくインストールしても有効になりません。[Windows Ghidra P0](docs/windows-ghidra-p0.md)を参照してください。
|
||||
|
||||
### トラブルシューティング
|
||||
|
||||
`npx -y rea-agents@latest doctor` はホスト、依存関係、解析ツール、エージェント設定を変更せずに確認します。構造化された診断には `--json` を追加してください。
|
||||
|
||||
Linux の既定ランチャーは `/opt/hopper/bin/Hopper` です。別の場所には `HOPPER_LAUNCHER_PATH` を設定します。ファイルがあるのに解析エンジンが見つからない場合は、`ldd /opt/hopper/bin/Hopper | grep 'not found'` で不足ライブラリを確認してください。詳しくは [Hopper ガイド](docs/installation.md#hopper)を参照してください。
|
||||
|
||||
### 更新とアンインストール
|
||||
|
||||
- `rea update` は現在の REA インストールを更新します。
|
||||
- `rea uninstall` は REA が管理するエージェント登録とワークフローファイルを削除します。Hopper は残ります。
|
||||
- `rea uninstall --purge-data` は REA のキャッシュと状態も削除します。それらを削除したい場合にだけ使ってください。
|
||||
|
||||
## 現在の対応状況
|
||||
|
||||
REA は CLI と MCP からネイティブバイナリ、JavaScript/Electron アプリ、.NET アセンブリ、Web サイトを解析できます。使える操作はホスト、対象、選択した解析ツールによって異なります。
|
||||
|
||||
- Hopper はネイティブ解析と注釈操作に対応します。GUI の動作はプラットフォームによって異なります。
|
||||
- Ghidra は Linux x64 と macOS x64/arm64 で、一覧、検索、逆コンパイル、アセンブリ、呼び出し、参照、命令、型の検査など 22 の読み取り専用操作を提供します。GUI 操作や変更操作はありません。
|
||||
- ブラウザー、Electron、プロセスの実行時ワークフローには、それぞれ設定、承認、終了処理の要件があります。詳細は [English README](README.md#current-status) を参照してください。
|
||||
- Windows の Ghidra 解析は未対応です。進捗は [#527](https://github.com/morluto/rea/issues/527) で確認できます。
|
||||
|
||||
## ひとつのプロンプトで調査を完結
|
||||
### エージェントに依頼する
|
||||
|
||||
```text
|
||||
メモアプリをリバースエンジニアリングし、オフライン検索機能の仕組みを説明して、
|
||||
TypeScript と SQLite を使って私のプロジェクト向けに実装してください。
|
||||
Notes アプリの検索機能の仕組みを調べ、根拠を示して、私のプロジェクトにも同様の機能を実装してください。
|
||||
```
|
||||
|
||||
| 手順 | エージェントの処理 | REA ツール |
|
||||
| ---: | ------------------------------ | ---------------------------------------------------------------- |
|
||||
| 1 | バイナリを開いて識別 | `open_binary`, `binary_overview` |
|
||||
| 2 | オフライン検索の手掛かりを探す | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | 手掛かりと実行コードを接続 | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | 制御フローを復元 | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | 関連する処理を逆コンパイル | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | プロジェクトに機能を実装する | 技術スタック、プロダクト、要件に合わせたコード |
|
||||
Notes を対象のアプリに置き換え、理解したい機能を指定してください。
|
||||
|
||||
REA は手順 1〜5 のバイナリ解析を処理し、手順 6 はエージェントの通常の編集・テストツールが行います。
|
||||
### ターミナルを使う
|
||||
|
||||
## エージェントにできること
|
||||
|
||||
- ソースコードがない機能の仕組みを説明する。
|
||||
- アプリの認証、保存、更新、ネットワークフローを復元する。
|
||||
- 非公開の形式やインターフェースを文書化できる構造を回収する。
|
||||
- 文字列やシンボルから疑わしい動作の実装コードまで追跡する。
|
||||
- 同じセッションで 2 バージョンを切り替え、実装経路を比較する。
|
||||
- 気になる機能を調査し、自分のプロダクトに合わせた形で実装する。
|
||||
- 復元した動作をプロダクト機能、テスト、移行ノート、移植、相互運用できる代替実装へ変換する。
|
||||
- Swift / Objective-C のメタデータを解析する。
|
||||
- Hopper に名前、コメント、ブックマークを残し、人間とエージェントの調査を共有する。
|
||||
|
||||
## 調査ツールカタログ
|
||||
|
||||
| ツール分類 | 数 | 用途 |
|
||||
| -------------------- | --: | --------------------------------------------------------------------------- |
|
||||
| バイナリ検査 | 39 | 関数、疑似コード、アセンブリ、文字列、シンボル、参照、注釈 |
|
||||
| 組み合わせた解析 | 14 | 概要、関数解析、一括逆コンパイル、呼び出しグラフ、Swift と ObjC |
|
||||
| macOS ネイティブ | 7 | Mach-O メタデータ、署名、plist、アーキテクチャ、Swift 名の復元 |
|
||||
| ファイルとパッケージ | 5 | ディレクトリとパッケージ、Interface Builder、Apple リソース、抽出 |
|
||||
| .NET PE/CLI | 7 | アセンブリ識別、メタデータ、CIL、ネイティブ呼び出し、比較、再構築の取り込み |
|
||||
| ブラウザー観察 | 9 | ページ、スクリプト、ソースマップ、WebMCP、スクリーンショット、比較 |
|
||||
| Electron 解析 | 5 | ページ観察、アプリ構造、静的・実行時結果の対応付け |
|
||||
| JavaScript 実行時 | 2 | 既存の Node/Electron Inspector に接続し、スクリプトと実行コンテキストを観察 |
|
||||
| アプリのワークフロー | 7 | 機能追跡、バージョン比較、戻り値構造の比較、再実装の検証 |
|
||||
| バイナリセッション | 21 | 対象切り替え、根拠の保存、プロセス・関数比較、未解決事項の記録 |
|
||||
|
||||
## ロードマップ
|
||||
|
||||
次の作業はネイティブ対象の検証拡大、JavaScript と .NET のバージョン比較、実行時の観察と再実装の検証です。Setup はエージェント連携と Hopper のインストールを選択できます。他の解析ツールのインストールは今後の作業です。[インストールのロードマップ](docs/roadmap.md)と[解析ツールの評価](docs/provider-evaluation.md)を参照してください。
|
||||
|
||||
## 他のコーディングエージェントで使う
|
||||
|
||||
Setup は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、VS Code に対応します。既存の REA 登録は初期選択され、それ以外の検出済みクライアントは選択が必要です。ローカル MCP サーバーに対応するエージェントは、次の設定でも接続できます。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@4.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## 仕組み
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["コーディングエージェント"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["ターミナル"] --> REA
|
||||
REA --> Hopper["Hopper 解析操作"]
|
||||
Hopper --> App["あなたのアプリ"]
|
||||
REA --> Ghidra["Ghidra 読み取り専用解析<br/>インベントリ + 関数解析"]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI と MCP サーバーは同じ解析処理を使います。ターミナルコマンドは完了後に自分のブリッジセッションを解放し、エージェントセッションは調査中の接続を維持できます。REA セッションを閉じても、ユーザーが利用中の Hopper は終了しません。
|
||||
|
||||
## CLI
|
||||
|
||||
上のエージェントワークフローが、REA を使う最も簡単な方法です。ターミナルから一度だけアプリの概要を調べる場合は、次を実行します。
|
||||
展開済みの JavaScript/Electron アプリのディレクトリ、または ASAR を調べます。
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
直接デコンパイルする方法やその他のオプションは、`npx -y rea-agents@latest --help` で確認できます。
|
||||
結果にはモジュール、インポート、Electron の境界と、それらの根拠が含まれます。パスは対象に合わせて変更してください。Windows では、たとえば `"D:/apps/example"` を指定します。
|
||||
|
||||
グローバルな `rea` コマンドとしてもインストールできます。
|
||||
日常的に使う場合は、`rea` コマンドをインストールできます。
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA は Mac の `.app` フォルダーを直接開けます。エージェントがアプリを見つけられない場合は、インストール場所を伝えてください。
|
||||
ネイティブ分析には、先にプロバイダーの設定が必要です。ネイティブ分析のコマンド、プロバイダーの選択、スナップショット、スクリプトでの利用は、[CLI と Evidence のガイド](docs/cli.md)を参照してください。
|
||||
|
||||
## Hopper アプリの動作
|
||||
### REA を更新する
|
||||
|
||||
REA は必要なときに Hopper を起動します。Hopper のランチャーは内部でアプリをアクティブ化するため、ターゲットを開くと Hopper が前面に出る場合があります。REA はバックグラウンド起動を要求しますが、常に背面に留まる保証はありません。
|
||||
REA は更新が速く、新しいリリースには頻繁にバグ修正が含まれます。インストールしたバージョンを最新に保ってください。
|
||||
|
||||
明示的な形式・アーキテクチャ引数により一般的な FAT / ARM 選択ダイアログを避けますが、別の Hopper / macOS ダイアログは人の操作を必要とする場合があります。セッションを閉じるとブリッジとソケットを削除しますが、ユーザーが利用中の Hopper は終了しません。
|
||||
npm でインストールした CLI の場合:
|
||||
|
||||
## セキュリティモデル
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
各セッションはランダムな capability token と現在のユーザーだけが使える Unix ソケットを使用します。Ghidra セッションは隔離された一時プロジェクトも使用し、ユーザー所有の Ghidra プロジェクトを開いたり変更したりしません。これはサンドボックスではなく、同じ OS ユーザー権限で動作する悪意あるプロセスを防御しません。信頼できないバイナリの解析は、現在のユーザー権限で選択されたローカルプロバイダーに委譲されます。脆弱性は [SECURITY.md](SECURITY.md) の非公開手順で報告してください。
|
||||
エージェントの登録とスキルを更新するには、更新処理が表示するセットアップコマンドを実行してください。
|
||||
|
||||
## FAQ
|
||||
`npx` を使っている場合は、次のコマンドでエージェントの設定を更新します。
|
||||
|
||||
<details><summary><strong>Hopper を先に起動する必要がありますか?</strong></summary>
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
いいえ。REA が必要時に起動します。Hopper が起動済みでも使えますが、既存の GUI ドキュメントには接続せず、新しい解析ドキュメントを開きます。
|
||||
設定の変更を確認し、エージェントを再起動してください。単発の CLI 操作では、`npx rea-agents@latest` に続けてコマンドを指定します。
|
||||
|
||||
## REA の仕組み
|
||||
|
||||
エージェントは MCP 経由で REA を呼び出し、対象を調べて関連するコードを追跡します。REA は調査結果と根拠を返します。エージェントはそれを使って追加の質問をしたり、動作を説明したり、実装を書いてテストしたりします。CLI コマンドも同じワークフローを使います。
|
||||
|
||||

|
||||
|
||||
[原寸大の図を開く](website/public/assets/figures/rea-investigation-flow.svg)。
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## 分析できる対象
|
||||
|
||||
REA には Node.js 22.x(>=22.19)、24.x(>=24.11)、または 26+ と npm が必要です。追加のツールや対応ホストは、対象によって異なります。
|
||||
|
||||
| 対象 | REA が返すもの | 要件とガイド |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ネイティブバイナリ | 疑似コード、アセンブリ、文字列、シンボル、呼び出し、参照 | Hopper、Ghidra、または IDA;[ネイティブ分析](https://rea.tools/guides/native/) |
|
||||
| オフライン ELF レイアウト | セクション、セグメント、元のシンボル/再配置情報、静的解析によるセキュリティ対策の候補 | Linux x64 で利用者が用意する pwntools;[バイナリ診断](docs/binary-diagnostics.md) |
|
||||
| EVM バイトコード | ディスパッチセレクター、バイトオフセット、推定された引数と状態変更の可否 | ローカルの生バイト/16 進数の入力ファイル;[オフライン EVM ガイド](docs/evm-bytecode.md) |
|
||||
| 記録済みの Linux クラッシュ | 生の note レコード、記録された全スレッドのレジスター/シグナル、任意のマッピング候補 | 利用者が用意する pwntools;GDB/pwndbg は任意;[記録済みクラッシュ](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | モジュール、インポート、ソースマップ、ルート、IPC、ネイティブアドオンの関係 | Node.js と npm;[アプリケーション分析](https://rea.tools/guides/javascript/) |
|
||||
| ウェブサイト | ページ構造、スクリプト、ネットワークの観測結果、依頼されたスクリーンショット | Chrome 系ブラウザー;[ブラウザー分析](https://rea.tools/guides/browser/) |
|
||||
| 保存済みのネットワークキャプチャ | リクエスト、レスポンス、取得可能なペイロード、ソース内の位置 | HAR;mitmproxy ネイティブ形式のキャプチャには Linux 上の mitmdump;[キャプチャガイド](docs/web-network-captures.md) |
|
||||
| .NET アセンブリ | メタデータ、CIL 命令、宣言されたネイティブ依存関係、ビルドの比較 | 静的な検査;[マネージドコードのガイド](docs/managed-code-analysis.md) |
|
||||
| Android APK | マニフェストの宣言、クラス、逆コンパイルされたメソッド、参照 | Linux/macOS 上のヘッドレス JADX と完全な JDK;[Android ガイド](docs/android-analysis.md) |
|
||||
| ファームウェア | 領域、抽出結果、ネイティブ分析への引き継ぎ | Linux 上の Binwalk / Unblob;[ファームウェアガイド](docs/firmware-analysis.md) |
|
||||
| パッケージとリソース | ファイル一覧、ダイジェスト、plist、Apple バンドルの構造、抽出されたリソース | [アーティファクトと JavaScript のガイド](docs/javascript-artifact-reconstruction.md)、[Apple アプリケーション](docs/apple-application-analysis.md) |
|
||||
| プロセスの動作 | ターミナル出力、対話、終了とファイルシステムの観測結果、実行の比較 | ネイティブ PTY を備えた Linux/macOS;[プロセスキャプチャ](docs/process-capture.md) |
|
||||
|
||||
静的な JavaScript と .NET の検査は、指定されたファイルを読み取り、アプリケーションを実行しません。実行時キャプチャは、あなたのユーザー権限で対象を実行したり、対象とやり取りしたりします。それぞれの実行時ガイドに、どのような操作が行われるかを記載しています。
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
対応するネイティブ形式とホストは、プロバイダーによって異なります。[Hopper と Ghidra のセットアップ](docs/installation.md#hopper)、[IDA ガイド](docs/ida-provider.md)、[Windows 向け Ghidra の実験的対応](docs/windows-ghidra-p0.md)を参照してください。Ghidra は [16 ビット DOS 分析](docs/ghidra-dos.md)にも対応しています。プロバイダーの選択は [CLI ガイド](docs/cli.md#choose-a-provider)を参照してください。最新の npm リリース以降に追加された機能については、[リリースでの利用可否](docs/installation.md#released-package-and-main)を確認してください。
|
||||
|
||||
## 事例
|
||||
|
||||
### DX-Ball:音声のパン計算を再構築する
|
||||
|
||||
音声の呼び出しから位置をパンに変換する補助関数をたどり、命令を調べ、不完全な疑似コードを C に書き直します。再構築した実装は、元の x86 に対する 3,205 ケースの検証を通過し、コンパイル後の関数の全 63 バイトを再現しています。
|
||||
|
||||
[事例を読む](https://rea.tools/showcase/dx-ball/) ·
|
||||
[再構築リポジトリ](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion:Electron のクリップボードブリッジを追跡する
|
||||
|
||||
レンダラーのクリップボード API を見つけ、preload と IPC を経由してメインプロセスまでたどり、リッチなクリップボード形式を調べます。
|
||||
|
||||
[事例を読む](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04:DOS の弾幕リングの計算を復元する
|
||||
|
||||
元の PC-98 ゲームの 16 ビット命令を調べ、固定角度と自機を狙う角度の計算を復元し、再構築した C++ を当時のコンパイラーの出力と比較します。
|
||||
|
||||
[事例を読む](https://rea.tools/showcase/th04/) ·
|
||||
[再構築リポジトリ](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
REA で興味深い対象を調べたら、ぜひ共有してください。[issue](https://github.com/morluto/rea/issues) または [pull request](https://github.com/morluto/rea/pulls) に、対象、調べたかったこと、REA がどう役立ったか、何が分かったかを記載してください。
|
||||
|
||||
## よくある質問
|
||||
|
||||
<details>
|
||||
<summary><strong>どのエージェントで REA を使えますか?</strong></summary>
|
||||
|
||||
ローカル MCP サーバーに対応するエージェントなら使えます。セットアップは[対応エージェント](docs/installation.md#supported-agents)を設定します。その他のクライアントでは、[MCP の手動登録](docs/installation.md#mcp-registry)を利用できます。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA に Hopper は含まれますか?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>Hopper、Ghidra、IDA は必要ですか?</strong></summary>
|
||||
|
||||
含まれません。Setup で Hopper をインストールできますが、Hopper は独自のライセンスを持つ別製品です。REA は CLI、MCP サーバー、エージェント向けワークフローを提供します。
|
||||
深いネイティブ分析には、そのいずれかを使います。静的な JavaScript と .NET の検査には、ネイティブ分析エンジンは必要ありません。セットアップは承認後に Hopper をインストールできます。Ghidra と IDA は既存のインストールを使います。[プロバイダーのセットアップ](docs/installation.md#hopper)を参照してください。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>バイナリはアップロードされますか?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>先に Hopper を起動する必要はありますか?</strong></summary>
|
||||
|
||||
REA にホスト型解析サービスはありません。ローカル Unix ソケット経由で Hopper を操作します。エージェントやモデル提供者のデータポリシーは別途確認してください。
|
||||
REA は必要な操作の際に Hopper を起動します。macOS では、初回起動時にデモモードを選ぶか、ライセンスを有効化するよう求められることがあります。[Hopper の起動とトラブルシューティング](docs/installation.md#launcher-paths-and-troubleshooting)を参照してください。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>元のソースコードを復元できますか?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>skills.sh からスキルをインストールすると何ができますか?</strong></summary>
|
||||
|
||||
保証できません。REA は疑似コード、アセンブリ、シンボル、文字列、メタデータ、関係を提供し、エージェントが観察した動作を説明または互換再現できるようにします。
|
||||
スキルはエージェントに調査の手順を提供します。`rea setup` で REA の MCP サーバーを登録し、対応する手順をインストールしてから、エージェントを再起動してください。[スキルのみのインストール](docs/installation.md#skill-only-installation)を参照してください。
|
||||
|
||||
</details>
|
||||
|
||||
## 開発
|
||||
<details>
|
||||
<summary><strong>REA はどのようなコードを返しますか?</strong></summary>
|
||||
|
||||
開発環境、アーキテクチャ、テスト、リリース手順は [CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。
|
||||
ネイティブ分析は疑似コードとアセンブリを返します。JavaScript/Electron 分析はモジュールとその関係を復元します。エージェントはその結果を使って実装を書き、テストします。[事例](#事例)に具体的な手順があります。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA はアプリをアップロードしますか?</strong></summary>
|
||||
|
||||
REA は対象をローカルで分析します。エージェントはツールの結果を受け取り、そのモデルプロバイダーには独自のデータポリシーがあります。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>バグに遭遇したらどうすればよいですか?</strong></summary>
|
||||
|
||||
まず更新してください。最近のリリースですでに修正されている可能性があります。
|
||||
|
||||
npm でインストールした CLI の場合:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`npx` でエージェントを設定している場合:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
エージェントを使っている場合は、[設定の更新](#rea-を更新する)を完了し、再起動してください。同じタスクをもう一度試し、問題が続く場合は [issue を作成](https://github.com/morluto/rea/issues)して、REA のバージョン、対象の種類、再現手順、エラー出力を記載してください。
|
||||
|
||||
</details>
|
||||
|
||||
## ドキュメント
|
||||
|
||||
まずはウェブサイトの[実践ガイド](https://rea.tools/guides/)を参照してください。正確なオプション、前提条件、結果の契約については、以下を参照してください。
|
||||
|
||||
- [インストールとセットアップ](docs/installation.md):エージェントの登録、プロバイダーの設定、更新、アンインストール。
|
||||
- [準備状況の確認とトラブルシューティング](docs/installation.md#check-readiness-for-your-task):特定のエージェントや分析エンジンを診断。
|
||||
- [CLI と Evidence](docs/cli.md):コマンド、プロバイダーの選択、スナップショット、インポート/エクスポート、終了ステータス。
|
||||
- [MCP 契約](docs/mcp-contracts.md)と[エージェント向けプロンプト](docs/mcp-prompts.md):ツールの結果、セッション、ガイド付き調査。
|
||||
- [ツールカタログ](docs/mcp-contracts.md#generated-catalog):ビルド時に生成されるツール、プロバイダー、CLI コマンドの一覧。
|
||||
- [ロードマップ](docs/roadmap.md):計画中の作業と機能の進捗。
|
||||
|
||||
脆弱性の報告は [SECURITY.md](SECURITY.md) に従ってください。
|
||||
|
||||
## 貢献する
|
||||
|
||||
REA への貢献を歓迎しますしてバグや機能の提案を報告したり、[pull request を送信](https://github.com/morluto/rea/pulls)してコードやドキュメントを改善したりできます。
|
||||
|
||||
開発環境とチェックは [CONTRIBUTING.md](CONTRIBUTING.md)、検証レーンは[テストガイド](docs/testing.md)、プロジェクト構造は[アーキテクチャ図](docs/architecture.mermaid)を参照してください。
|
||||
|
||||
## プロジェクトリンク
|
||||
|
||||
[ウェブサイト](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [セキュリティ](SECURITY.md)
|
||||
|
||||
## スター履歴
|
||||
|
||||
🎉 **GitHub スター 30,000 件、ありがとうございます!**
|
||||
|
||||
REA の利用、バグ報告、機能の要望、ビルドのテスト、修正への貢献に感謝します。
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA の GitHub スター履歴" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 免責事項
|
||||
|
||||
REA は、合法的なリバースエンジニアリングの研究、分析、再構築のためのツールを提供します。必要な許可を得て、適用される法律に従う責任は利用者にあります。このプロジェクトは違法または無許可の利用を支持しません。
|
||||
|
||||
## ライセンス
|
||||
|
||||
|
||||
+198
-216
@@ -1,304 +1,286 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [日本語](README_ja.md) · **한국어** · [العربية](README_ar.md)
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · **한국어** · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: 무엇이든 리버스 엔지니어링
|
||||
|
||||
### 에이전트로 앱의 동작부터 네이티브 바이너리까지 조사하세요.
|
||||
### 하나의 MCP로 바이너리, 애플리케이션, 런타임 동작을 리버스 엔지니어링합니다.
|
||||
|
||||
**마음에 드는 기능을 찾고, 작동 방식을 이해하고, 원하는 방식으로 구현하세요.**
|
||||
**마음에 드는 기능을 발견했다면, 바이너리 수준까지 작동 원리를 이해하세요.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#조사-도구-카탈로그)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[빠른 시작](#빠른-시작) · [현재 지원 범위](#현재-지원-범위) · [바이너리에서 동작까지](#바이너리에서-동작까지) · [조사 도구 카탈로그](#조사-도구-카탈로그) · [로드맵](#로드맵) · [작동 방식](#작동-방식)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
<table aria-label="REA community">
|
||||
**[웹사이트](https://rea.tools/) · [가이드](https://rea.tools/guides/) · [사례](https://rea.tools/showcase/)**
|
||||
|
||||
[빠른 시작](#빠른-시작) · [REA의 작동 방식](#rea의-작동-방식) · [분석할 수 있는 대상](#분석할-수-있는-대상) · [사례](#사례) · [자주 묻는 질문](#자주-묻는-질문) · [문서](#문서)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA가 Hopper 안에서 분석 브리지를 실행해 네이티브 바이너리를 검사하는 모습" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA 커뮤니티">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>리버스 엔지니어링 커뮤니티에 참여하세요</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 질문과 답변 · 작업 공유</sub>
|
||||
<sub>Discord · 질문과 답변 · 결과 공유</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper에서 분석 브리지를 시작하고 네이티브 바이너리를 조사하는 REA" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
앱에서 마음에 드는 기능을 찾았다면 에이전트에게 REA로 조사해 달라고 요청하세요. 소스 코드가 없어도 앱을 분석하고, 기능의 동작과 근거를 설명한 뒤, 프로젝트에 맞는 기능을 구현할 수 있습니다.
|
||||
앱에서 내 제품에도 넣고 싶은 기능을 발견했나요? 에이전트에게 REA로 조사해 달라고 요청하세요. 소스 코드가 없어도 앱을 검사하고, 기능의 작동 원리를 설명하고, 근거를 보여 주고, 프로젝트에 맞는 기능을 구현할 수 있습니다.
|
||||
|
||||
REA는 네이티브 바이너리, JavaScript/Electron 앱, .NET 어셈블리, 웹사이트 분석 도구를 제공합니다. 에이전트와 터미널에서 같은 도구를 사용할 수 있습니다. 분석은 로컬에서 실행되며 결과에는 근거와 제한 사항이 포함됩니다.
|
||||
REA는 에이전트를 분석 도구에 연결해 네이티브 바이너리, JavaScript와 Electron 앱, .NET 어셈블리, 웹사이트를 검사할 수 있게 합니다. 터미널에서도 같은 도구를 사용할 수 있습니다. 분석은 로컬에서 실행되며, 결과에는 각 결론의 근거와 한계가 포함됩니다.
|
||||
|
||||
Setup은 에이전트를 설정하고 기존 Hopper 또는 Ghidra 설치에 연결합니다. 승인 후 Hopper를 설치할 수도 있습니다.
|
||||
설정 과정은 에이전트에 REA를 등록하고 해당 버전에 맞는 워크플로 지침을 설치합니다. 네이티브 분석에는 기존 Hopper 또는 Ghidra 설치를 사용할 수 있습니다. 승인하면 설정 과정에서 Hopper를 설치할 수도 있습니다. 정적 JavaScript 분석에는 두 엔진 모두 필요하지 않습니다.
|
||||
|
||||
## 에이전트에게 바로 요청하기
|
||||
|
||||
[설정](#빠른-시작)을 마친 뒤 에이전트를 다시 시작하고 요청하세요.
|
||||
|
||||
```text
|
||||
메모 앱의 검색 기능이 어떻게 동작하는지 조사하고 근거를 보여 주세요.
|
||||
그다음 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```
|
||||
|
||||
메모 앱 대신 조사할 앱을 지정하거나 먼저 개요를 요청할 수 있습니다.
|
||||
|
||||
## 바이너리에서 동작까지
|
||||
|
||||
| 디컴파일 | 이해 | 재현 |
|
||||
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| 네이티브 앱이나 실행 파일에서 프로시저, 의사 코드, 어셈블리, 문자열, 심볼, 세그먼트, 메타데이터를 복구합니다. | 호출자, 피호출자, 상호 참조, 호출 그래프를 따라 기능이나 알고리즘의 실제 동작을 설명합니다. | 에이전트가 배운 내용을 사용자의 기술 스택, 화면, 요구 사항에 맞는 제품 기능으로 만듭니다. |
|
||||
|
||||
REA는 분석을 바이너리 증거에 근거하게 합니다. 원본 소스 코드를 복원하거나 앱 전체를 자동으로 복제한다고 주장하지 않습니다.
|
||||
|
||||
## REA를 사용하는 이유
|
||||
|
||||
| | |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| **에이전트용** | 컴파일된 앱에 관해 질문하고 추측 대신 증거를 수집하게 합니다. |
|
||||
| **CLI와 MCP** | 터미널과 코딩 에이전트에서 동일한 리버스 엔지니어링 기능을 사용합니다. |
|
||||
| **복잡성 처리** | 도구 설정, 앱 열기, 조사 유지, 작업 후 정리를 REA가 처리합니다. |
|
||||
| **전체 조사 과정** | 첫 개요에서 의사 코드, 호출 관계, 타입, 구현 단서까지 이어서 조사합니다. |
|
||||
| **로컬 분석** | 분석은 지원되는 로컬 호스트에서 실행되며 REA는 바이너리를 호스팅 분석 서비스에 업로드하지 않습니다. |
|
||||
| **컨텍스트 유지** | 질문마다 분석을 처음부터 시작하지 않고 여러 바이너리를 연속으로 조사합니다. |
|
||||
> **[REA 웹사이트](https://rea.tools/)**에서 설정 방법, 그림으로 설명한 가이드, 실제 사례를 확인하세요.
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 설정 실행하기(권장)
|
||||
### 에이전트 설정
|
||||
|
||||
REA를 에이전트에 연결하세요.
|
||||
Node.js와 npm이 설치된 환경에서 다음 명령을 실행하세요.
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Setup은 먼저 연결할 에이전트를 여러 개 선택하도록 안내합니다. 기존 REA 등록은 기본 선택되며, 감지만 된 클라이언트는 자동 선택되지 않습니다. 아직 설정되지 않은 클라이언트도 직접 선택할 수 있습니다. 정확한 경로와 변경 사항을 검토한 뒤 승인하세요. 선택한 에이전트에는 기본적으로 REA 워크플로가 설치됩니다. Hopper는 별도의 선택 사항이며 따로 승인해야 합니다. 기존 Ghidra 경로도 등록할 수 있습니다.
|
||||
에이전트를 선택하고, 예정된 변경 사항을 검토한 뒤 승인하세요. 설정 과정은 기존 설정을 백업하고 REA의 MCP 서버와 버전에 맞는 워크플로 지침을 추가합니다. 완료 후 에이전트를 다시 시작하세요.
|
||||
|
||||
변경 전에 계획을 표시하고 기존 설정을 백업합니다. 요구 사항과 추가 옵션은 [설치 안내](docs/installation.md)를 참고하세요.
|
||||
설정은 Claude Code, Codex, Cursor, Gemini CLI, Grok Build 및 [다른 에이전트](docs/installation.md#supported-agents)를 지원합니다. 제공자 설정과 수동 MCP 등록은 [설치 및 설정](docs/installation.md)을 참고하세요.
|
||||
|
||||
### 에이전트에서 사용하기
|
||||
|
||||
설정 후 에이전트를 다시 시작하고 조사할 앱이나 기능을 설명하세요. REA는 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 다른 에이전트는 아래 MCP 설정을 사용할 수 있습니다.
|
||||
|
||||
Hopper는 데모 모드로 사용할 수 있습니다. 첫 실행 안내가 나오면 데모를 선택하거나 기존 라이선스를 입력하세요.
|
||||
|
||||
### 터미널에서 실행하기
|
||||
|
||||
설정 후 실행하세요.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
### rea 명령 설치하기
|
||||
|
||||
명령줄 도구를 설치하세요.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js와 npm이 먼저 설치되어 있어야 합니다. 터미널에서 실행하면 설치 프로그램이 `rea`를 설치하고 설정을 시작합니다.
|
||||
|
||||
npm으로 설치한 뒤 설정을 실행할 수도 있습니다.
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 요구 사항
|
||||
|
||||
- macOS 12 이상
|
||||
- Ubuntu 24.04+, Fedora 41+ 또는 64비트 Arch Linux
|
||||
- Node.js 22.19+ 또는 24.11+와 npm
|
||||
|
||||
네이티브 바이너리 분석에는 Hopper 또는 Ghidra가 필요합니다. Hopper는 별도 소프트웨어입니다. 데모에는 공급업체의 제한이 있지만 유료 라이선스가 필수는 아닙니다.
|
||||
|
||||
Ghidra는 Linux x64와 macOS x64/arm64를 지원합니다. Ghidra 12.1.4와 완전한 64비트 JDK 21을 별도로 설치한 뒤 REA가 사용하도록 설정하세요. macOS에서는 호스트 아키텍처에 맞는 네이티브 디컴파일러도 필요합니다.
|
||||
|
||||
Setup은 설치를 확인하고 경로를 저장할 수 있습니다. Ghidra, Java, Node.js, npm, Homebrew를 설치하거나 업데이트하지 않습니다.
|
||||
|
||||
Windows Ghidra 분석은 사용할 수 없습니다. 프로세스 소유권, 비공개 디렉터리 권한, 안전한 경로 검사가 구현되지 않았으므로 Ghidra와 Java를 올바르게 설치해도 분석을 활성화할 수 없습니다. [Windows Ghidra P0](docs/windows-ghidra-p0.md)를 참고하세요.
|
||||
|
||||
### 문제 해결
|
||||
|
||||
`npx -y rea-agents@latest doctor`는 호스트, 의존성, 분석 도구, 에이전트 설정을 변경 없이 확인합니다. 구조화된 진단에는 `--json`을 추가하세요.
|
||||
|
||||
Linux의 기본 Hopper 실행 파일은 `/opt/hopper/bin/Hopper`입니다. 다른 위치에는 `HOPPER_LAUNCHER_PATH`를 설정하세요. 파일이 있는데도 분석 엔진이 없다고 하면 `ldd /opt/hopper/bin/Hopper | grep 'not found'`로 누락된 라이브러리를 확인하세요. 자세한 내용은 [Hopper 안내](docs/installation.md#hopper)를 참고하세요.
|
||||
|
||||
### 업데이트와 제거
|
||||
|
||||
- `rea update`는 현재 REA 설치를 업데이트합니다.
|
||||
- `rea uninstall`은 REA가 관리하는 에이전트 등록과 워크플로 파일을 제거합니다. Hopper는 유지합니다.
|
||||
- `rea uninstall --purge-data`는 REA 캐시와 상태도 삭제합니다. 해당 데이터를 지우려는 경우에만 사용하세요.
|
||||
|
||||
## 현재 지원 범위
|
||||
|
||||
REA는 CLI와 MCP로 네이티브 바이너리, JavaScript/Electron 앱, .NET 어셈블리, 웹사이트를 분석합니다. 사용할 수 있는 작업은 호스트, 대상, 선택한 분석 도구에 따라 다릅니다.
|
||||
|
||||
- Hopper는 네이티브 분석과 주석 작업을 지원합니다. GUI 동작은 플랫폼에 따라 다릅니다.
|
||||
- Ghidra는 Linux x64와 macOS x64/arm64에서 목록, 검색, 디컴파일, 어셈블리, 호출, 참조, 명령어, 타입 검사 등 읽기 전용 작업 22개를 제공합니다. GUI나 변경 작업은 제공하지 않습니다.
|
||||
- 브라우저, Electron, 프로세스 런타임 워크플로에는 각각 설정, 승인, 종료 처리 요구 사항이 있습니다. 자세한 내용은 [English README](README.md#current-status)를 참고하세요.
|
||||
- Windows Ghidra 분석은 아직 사용할 수 없습니다. 진행 상황은 [#527](https://github.com/morluto/rea/issues/527)에서 확인하세요.
|
||||
|
||||
## 하나의 프롬프트로 전체 조사
|
||||
### 에이전트에게 요청
|
||||
|
||||
```text
|
||||
메모 앱을 리버스 엔지니어링해 오프라인 검색 기능의 작동 방식을 설명한 다음,
|
||||
TypeScript와 SQLite를 사용해 제 프로젝트에 맞는 버전을 구현해 주세요.
|
||||
Notes 앱의 검색 기능이 어떻게 작동하는지 조사하고, 근거를 보여 준 다음, 내 프로젝트에 비슷한 기능을 구현해 줘.
|
||||
```
|
||||
|
||||
| 단계 | 에이전트 작업 | REA 도구 |
|
||||
| ---: | ----------------------- | ---------------------------------------------------------------- |
|
||||
| 1 | 바이너리 열기 및 식별 | `open_binary`, `binary_overview` |
|
||||
| 2 | 오프라인 검색 단서 찾기 | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | 단서를 실행 코드에 연결 | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | 제어 흐름 재구성 | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | 관련 루틴 디컴파일 | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | 프로젝트에 기능 구현 | 기술 스택, 제품, 요구 사항에 맞는 코드 |
|
||||
Notes를 대상 앱으로 바꾸고 이해하고 싶은 기능을 지정하세요.
|
||||
|
||||
REA는 1–5단계의 바이너리 분석을 처리합니다. 6단계는 에이전트가 일반 파일 편집 및 테스트 도구로 수행합니다.
|
||||
### 터미널 사용
|
||||
|
||||
## 에이전트가 할 수 있는 일
|
||||
|
||||
- 소스 코드가 없는 기능의 동작을 설명합니다.
|
||||
- 앱의 인증, 저장소, 업데이트 또는 네트워크 흐름을 재구성합니다.
|
||||
- 문서화되지 않은 형식이나 인터페이스의 구조를 복구합니다.
|
||||
- 문자열이나 심볼에서 의심스러운 동작을 구현한 코드까지 추적합니다.
|
||||
- 한 세션에서 두 앱 버전을 전환하며 구현 경로를 비교합니다.
|
||||
- 마음에 드는 기능을 조사하고 자신의 제품에 맞는 버전으로 구현합니다.
|
||||
- 복구한 동작을 제품 기능, 테스트, 마이그레이션 문서, 포팅 또는 상호 운용 가능한 대체품으로 변환합니다.
|
||||
- Swift 및 Objective-C 메타데이터를 분석합니다.
|
||||
- Hopper에 이름, 주석, 북마크를 남겨 사람과 에이전트의 분석을 연결합니다.
|
||||
|
||||
## 조사 도구 카탈로그
|
||||
|
||||
| 도구 분류 | 수 | 용도 |
|
||||
| ----------------- | --: | -------------------------------------------------------------------- |
|
||||
| 바이너리 검사 | 39 | 함수, 의사 코드, 어셈블리, 문자열, 심볼, 참조, 주석 |
|
||||
| 결합 분석 | 14 | 개요, 함수 분석, 일괄 디컴파일, 호출 그래프, Swift와 ObjC 검사 |
|
||||
| macOS 네이티브 | 7 | Mach-O 메타데이터, 서명, plist, 아키텍처, Swift 이름 복원 |
|
||||
| 파일과 패키지 | 5 | 디렉터리와 패키지 검사, Interface Builder, Apple 리소스, 추출 |
|
||||
| .NET PE/CLI | 7 | 어셈블리 식별, 메타데이터, CIL, 네이티브 호출, 비교, 재구성 가져오기 |
|
||||
| 브라우저 관찰 | 9 | 페이지, 스크립트, 소스 맵, WebMCP, 스크린샷, 캡처 비교 |
|
||||
| Electron 분석 | 5 | 페이지 관찰, 앱 구조, 정적·런타임 결과 연결 |
|
||||
| JavaScript 런타임 | 2 | 기존 Node/Electron Inspector에 연결해 스크립트와 실행 컨텍스트 관찰 |
|
||||
| 앱 워크플로 | 7 | 기능 추적, 버전 비교, 반환 구조 비교, 재구현 검증 |
|
||||
| 바이너리 세션 | 21 | 대상 전환, 근거 저장, 프로세스·함수 비교, 미해결 항목 기록 |
|
||||
|
||||
## 로드맵
|
||||
|
||||
다음 작업은 네이티브 대상 검증 확대, JavaScript와 .NET의 버전 비교, 런타임 관찰과 재구현 검증입니다. Setup은 에이전트 연결과 Hopper 설치 선택을 이미 지원합니다. 다른 분석 도구 설치는 향후 작업입니다. [설치 로드맵](docs/roadmap.md)과 [분석 도구 평가](docs/provider-evaluation.md)를 참고하세요.
|
||||
|
||||
## 다른 코딩 에이전트에서 사용하기
|
||||
|
||||
Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 로컬 MCP 서버를 지원하는 에이전트는 다음 설정으로 연결할 수 있습니다.
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@4.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## 작동 방식
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["코딩 에이전트"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["터미널"] --> REA
|
||||
REA --> Hopper["Hopper 분석 작업"]
|
||||
Hopper --> App["사용자의 앱"]
|
||||
REA --> Ghidra["Ghidra 읽기 전용 분석<br/>인벤토리 + 함수 분석"]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI와 MCP 서버는 같은 분석 워크플로를 사용합니다. 터미널 명령은 완료 후 자신의 브리지 세션을 해제하고, 에이전트 세션은 조사 중 연결을 유지할 수 있습니다. REA 세션을 닫아도 사용자가 이용 중인 Hopper 앱은 종료하지 않습니다.
|
||||
|
||||
## CLI
|
||||
|
||||
위의 에이전트 워크플로가 REA를 사용하는 가장 쉬운 방법입니다. 터미널에서 앱을 한 번 빠르게 살펴보려면 다음을 실행하세요.
|
||||
압축을 푼 JavaScript/Electron 앱 디렉터리 또는 ASAR를 검사합니다.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
직접 디컴파일하는 방법과 기타 옵션은 `npx -y rea-agents@latest --help`에서 확인할 수 있습니다.
|
||||
결과에는 모듈, 임포트, Electron 경계와 관련 근거가 포함됩니다. 경로를 분석할 대상으로 바꾸세요. Windows에서는 `"D:/apps/example"` 같은 경로를 사용할 수 있습니다.
|
||||
|
||||
전역 `rea` 명령으로 설치할 수도 있습니다.
|
||||
자주 사용할 경우 `rea` 명령을 설치하세요.
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA는 Mac의 `.app` 폴더를 직접 열 수 있습니다. 에이전트가 앱을 찾지 못하면 설치 위치를 알려 주세요.
|
||||
네이티브 분석에는 먼저 제공자를 설정해야 합니다. 네이티브 명령, 제공자 선택, 스냅샷, 스크립트 사용은 [CLI 및 Evidence 가이드](docs/cli.md)를 참고하세요.
|
||||
|
||||
## Hopper 앱 동작
|
||||
### REA 업데이트
|
||||
|
||||
REA는 필요할 때 Hopper를 시작합니다. Hopper 런처는 내부적으로 앱을 활성화하므로 대상을 열 때 Hopper가 다른 창 앞으로 나타날 수 있습니다. REA는 백그라운드 시작을 요청하지만 항상 뒤에 머무르는 것을 보장할 수 없습니다.
|
||||
REA는 빠르게 바뀌며 새 릴리스에는 버그 수정이 자주 포함됩니다. 설치된 버전을 최신으로 유지하세요.
|
||||
|
||||
명시적인 형식과 아키텍처 인자로 일반적인 FAT/ARM 선택 대화상자를 피하지만, 다른 Hopper 또는 macOS 대화상자에는 사람이 응답해야 할 수 있습니다. 세션 종료 시 브리지와 소켓 디렉터리를 제거하지만 사용자가 작업 중인 Hopper 앱은 종료하지 않습니다.
|
||||
npm으로 설치한 CLI에서는 다음 명령을 사용합니다.
|
||||
|
||||
## 보안 모델
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
각 세션은 무작위 capability token과 현재 사용자 전용 Unix 소켓을 사용합니다. Ghidra 세션은 격리된 임시 프로젝트도 사용하며 사용자 소유 Ghidra 프로젝트를 열거나 수정하지 않습니다. 이는 샌드박스가 아니며 같은 운영 체제 사용자로 실행되는 악성 프로세스를 방어하지 않습니다. 신뢰할 수 없는 바이너리를 열면 선택한 로컬 공급자가 현재 사용자 권한으로 분석합니다. 취약점은 [SECURITY.md](SECURITY.md)의 비공개 절차로 신고하세요.
|
||||
에이전트 등록과 스킬을 갱신하려면 업데이트 과정에서 출력된 설정 명령을 실행하세요.
|
||||
|
||||
## FAQ
|
||||
`npx`를 사용한다면 다음 명령으로 에이전트 설정을 갱신하세요.
|
||||
|
||||
<details><summary><strong>Hopper를 미리 실행해야 하나요?</strong></summary>
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
아니요. REA가 필요할 때 시작합니다. Hopper가 실행 중이어도 사용할 수 있지만 기존 GUI 문서에 연결하는 대신 새 분석 문서를 엽니다.
|
||||
설정 변경 사항을 검토하고 에이전트를 다시 시작하세요. 일회성 CLI 작업에는 `npx rea-agents@latest` 뒤에 필요한 명령을 붙여 실행하세요.
|
||||
|
||||
## REA의 작동 방식
|
||||
|
||||
에이전트는 MCP를 통해 REA를 호출하여 대상을 검사하고 관련 코드를 추적합니다. REA는 조사 결과와 근거를 반환합니다. 에이전트는 이를 바탕으로 추가 질문을 하고, 동작을 설명하거나, 구현을 작성하고 테스트합니다. CLI 명령도 같은 워크플로를 사용합니다.
|
||||
|
||||

|
||||
|
||||
[원본 크기의 그림 열기](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## 분석할 수 있는 대상
|
||||
|
||||
REA에는 Node.js 22.x(>=22.19), 24.x(>=24.11) 또는 26+와 npm이 필요합니다. 추가 도구와 호스트 지원은 대상에 따라 다릅니다.
|
||||
|
||||
| 대상 | REA가 반환하는 내용 | 요구 사항 및 가이드 |
|
||||
| --------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 네이티브 바이너리 | 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조 | Hopper, Ghidra 또는 IDA; [네이티브 분석](https://rea.tools/guides/native/) |
|
||||
| 오프라인 ELF 레이아웃 | 섹션, 세그먼트, 원본 심볼/재배치 정보, 정적 분석으로 얻은 보안 완화 기법 후보 | Linux x64에서 호출자가 제공하는 pwntools; [바이너리 진단](docs/binary-diagnostics.md) |
|
||||
| EVM 바이트코드 | 디스패치 선택자, 바이트 오프셋, 추론한 인자와 상태 변경 가능 여부 | 로컬 원시 바이트/16진수 입력 파일; [오프라인 EVM 가이드](docs/evm-bytecode.md) |
|
||||
| 기록된 Linux 크래시 | 원시 note 레코드, 기록된 모든 스레드의 레지스터/시그널, 선택적 매핑 후보 | 호출자가 제공하는 pwntools; GDB/pwndbg는 선택 사항; [기록된 크래시](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | 모듈, 임포트, 소스 맵, 라우트, IPC, 네이티브 애드온 관계 | Node.js와 npm; [애플리케이션 분석](https://rea.tools/guides/javascript/) |
|
||||
| 웹사이트 | 페이지 구조, 스크립트, 네트워크 관찰 결과, 요청한 스크린샷 | Chrome 계열 브라우저; [브라우저 분석](https://rea.tools/guides/browser/) |
|
||||
| 저장된 네트워크 캡처 | 요청, 응답, 접근 가능한 페이로드, 소스 위치 | HAR; mitmproxy 네이티브 캡처에는 Linux의 mitmdump; [캡처 가이드](docs/web-network-captures.md) |
|
||||
| .NET 어셈블리 | 메타데이터, CIL 명령, 선언된 네이티브 의존성, 빌드 비교 | 정적 검사; [관리 코드 가이드](docs/managed-code-analysis.md) |
|
||||
| Android APK | 매니페스트 선언, 클래스, 디컴파일된 메서드, 참조 | Linux/macOS의 헤드리스 JADX와 전체 JDK; [Android 가이드](docs/android-analysis.md) |
|
||||
| 펌웨어 | 영역, 추출 결과, 네이티브 분석으로 전달할 정보 | Linux의 Binwalk / Unblob; [펌웨어 가이드](docs/firmware-analysis.md) |
|
||||
| 패키지 및 리소스 | 파일 목록, 다이제스트, plist, Apple 번들 구조, 추출한 리소스 | [아티팩트 및 JavaScript 가이드](docs/javascript-artifact-reconstruction.md), [Apple 애플리케이션](docs/apple-application-analysis.md) |
|
||||
| 프로세스 동작 | 터미널 출력, 상호작용, 종료 및 파일 시스템 관찰 결과, 실행 비교 | 네이티브 PTY를 지원하는 Linux/macOS; [프로세스 캡처](docs/process-capture.md) |
|
||||
|
||||
정적 JavaScript 및 .NET 검사는 제공된 파일을 읽으며 애플리케이션을 실행하지 않습니다. 런타임 캡처는 사용자의 권한으로 선택한 대상을 실행하거나 대상과 상호작용합니다. 각 런타임 가이드에서 그 영향을 설명합니다.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
지원하는 네이티브 형식과 호스트는 제공자마다 다릅니다. [Hopper 및 Ghidra 설정](docs/installation.md#hopper), [IDA 가이드](docs/ida-provider.md), [실험적 Windows Ghidra 지원](docs/windows-ghidra-p0.md)을 참고하세요. Ghidra는 [16비트 DOS 분석](docs/ghidra-dos.md)도 지원합니다. 제공자 선택은 [CLI 가이드](docs/cli.md#choose-a-provider)를 참고하세요. 최신 npm 릴리스 이후 추가된 기능은 [릴리스에서의 사용 가능 여부](docs/installation.md#released-package-and-main)를 확인하세요.
|
||||
|
||||
## 사례
|
||||
|
||||
### DX-Ball: 사운드 패닝 계산 재구현
|
||||
|
||||
사운드 호출에서 위치를 패닝 값으로 변환하는 보조 함수까지 추적하고, 명령을 검사하여 불완전한 의사 코드를 C로 옮깁니다. 재구현한 코드는 원본 x86에 대한 3,205개 테스트 케이스를 통과했으며, 컴파일된 함수의 63바이트 전체를 재현합니다.
|
||||
|
||||
[사례 읽기](https://rea.tools/showcase/dx-ball/) ·
|
||||
[재구현 저장소](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: Electron 클립보드 브리지 추적
|
||||
|
||||
렌더러의 클립보드 API를 찾고, preload와 IPC를 거쳐 메인 프로세스까지 추적한 뒤, 서식 있는 클립보드 형식을 검사합니다.
|
||||
|
||||
[사례 읽기](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: DOS 원형 탄막 계산 복원
|
||||
|
||||
원본 PC-98 게임의 16비트 명령을 검사하고 고정 각도와 조준 각도의 계산을 복원합니다. 재구현한 C++를 당시 컴파일러의 출력과 비교합니다.
|
||||
|
||||
[사례 읽기](https://rea.tools/showcase/th04/) ·
|
||||
[재구현 저장소](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
REA로 흥미로운 대상을 분석했다면 알려 주세요. [issue](https://github.com/morluto/rea/issues) 또는 [pull request](https://github.com/morluto/rea/pulls)에 대상, 조사하려던 질문, REA가 어떻게 도움이 되었는지, 무엇을 알아냈는지를 담아 공유하세요.
|
||||
|
||||
## 자주 묻는 질문
|
||||
|
||||
<details>
|
||||
<summary><strong>어떤 에이전트에서 REA를 사용할 수 있나요?</strong></summary>
|
||||
|
||||
로컬 MCP 서버를 지원하는 에이전트라면 사용할 수 있습니다. 설정 과정은 [지원하는 에이전트](docs/installation.md#supported-agents)를 구성합니다. 다른 클라이언트는 [수동 MCP 등록](docs/installation.md#mcp-registry)을 이용할 수 있습니다.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA에 Hopper가 포함되나요?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>Hopper, Ghidra 또는 IDA가 필요한가요?</strong></summary>
|
||||
|
||||
아니요. Setup이 Hopper를 설치할 수 있지만 Hopper는 자체 라이선스가 필요한 별도 소프트웨어입니다. REA는 CLI, MCP 서버, 에이전트용 워크플로를 제공합니다.
|
||||
심층 네이티브 분석에는 이 중 하나를 사용합니다. 정적 JavaScript 및 .NET 검사에는 네이티브 분석 엔진이 필요하지 않습니다. 설정 과정에서 승인 후 Hopper를 설치할 수 있으며, Ghidra와 IDA는 기존 설치를 사용합니다. [제공자 설정](docs/installation.md#hopper)을 참고하세요.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>바이너리가 업로드되나요?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>Hopper를 먼저 실행해야 하나요?</strong></summary>
|
||||
|
||||
REA에는 호스팅 분석 서비스가 없습니다. 로컬 Unix 소켓을 통해 Hopper를 조작합니다. 에이전트나 모델 공급자의 데이터 정책은 별도로 확인하세요.
|
||||
REA는 작업에 필요할 때 Hopper를 실행합니다. macOS에서는 처음 실행할 때 데모 모드를 선택하거나 라이선스를 활성화하라는 대화 상자가 나타날 수 있습니다. [Hopper 실행 및 문제 해결](docs/installation.md#launcher-paths-and-troubleshooting)을 참고하세요.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>원본 소스 코드를 복구할 수 있나요?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>skills.sh에서 설치하는 스킬은 어떤 역할을 하나요?</strong></summary>
|
||||
|
||||
보장할 수 없습니다. REA는 의사 코드, 어셈블리, 심볼, 문자열, 메타데이터 및 관계를 제공하여 에이전트가 관찰된 동작을 설명하거나 호환되게 재현하도록 돕습니다.
|
||||
스킬은 에이전트에게 조사 지침을 제공합니다. `rea setup`으로 REA의 MCP 서버를 등록하고 버전에 맞는 지침을 설치한 뒤 에이전트를 다시 시작하세요. [스킬만 설치하기](docs/installation.md#skill-only-installation)를 참고하세요.
|
||||
|
||||
</details>
|
||||
|
||||
## 개발
|
||||
<details>
|
||||
<summary><strong>REA는 어떤 코드를 반환하나요?</strong></summary>
|
||||
|
||||
개발 환경, 아키텍처, 테스트, 릴리스 지침은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참고하세요.
|
||||
네이티브 분석은 의사 코드와 어셈블리를 반환합니다. JavaScript/Electron 분석은 모듈과 그 관계를 복원합니다. 에이전트는 이 결과를 사용해 구현을 작성하고 테스트합니다. [사례](#사례)에서 구체적인 예를 볼 수 있습니다.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA가 내 앱을 업로드하나요?</strong></summary>
|
||||
|
||||
REA는 대상을 로컬에서 분석합니다. 에이전트는 도구 결과를 받으며, 에이전트의 모델 제공자에는 별도의 데이터 정책이 있습니다.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>버그가 발생하면 어떻게 해야 하나요?</strong></summary>
|
||||
|
||||
먼저 업데이트하세요. 최근 릴리스에서 이미 수정되었을 수 있습니다.
|
||||
|
||||
npm으로 설치한 CLI에서는 다음 명령을 사용합니다.
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`npx`로 에이전트를 설정했다면 다음 명령을 사용합니다.
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
에이전트를 사용 중이라면 [설정 갱신](#rea-업데이트)을 완료하고 다시 시작하세요. 같은 작업을 다시 시도하세요. 문제가 계속되면 REA 버전, 대상 유형, 재현 단계, 오류 출력을 포함해 [issue를 등록](https://github.com/morluto/rea/issues)하세요.
|
||||
|
||||
</details>
|
||||
|
||||
## 문서
|
||||
|
||||
웹사이트의 [실습 가이드](https://rea.tools/guides/)부터 읽어 보세요. 정확한 옵션, 사전 요구 사항, 결과 계약은 다음 문서를 참고하세요.
|
||||
|
||||
- [설치 및 설정](docs/installation.md): 에이전트 등록, 제공자 설정, 업데이트, 제거.
|
||||
- [준비 상태 확인 및 문제 해결](docs/installation.md#check-readiness-for-your-task): 특정 에이전트 또는 분석 엔진 진단.
|
||||
- [CLI 및 Evidence](docs/cli.md): 명령, 제공자 선택, 스냅샷, 가져오기/내보내기, 종료 상태.
|
||||
- [MCP 계약](docs/mcp-contracts.md) 및 [에이전트 프롬프트](docs/mcp-prompts.md): 도구 결과, 세션, 안내에 따른 조사.
|
||||
- [도구 카탈로그](docs/mcp-contracts.md#generated-catalog): 빌드 시 생성되는 도구, 제공자, CLI 명령 목록.
|
||||
- [로드맵](docs/roadmap.md): 계획된 작업과 기능 진행 상황.
|
||||
|
||||
취약점은 [SECURITY.md](SECURITY.md)에 따라 보고하세요.
|
||||
|
||||
## 기여
|
||||
|
||||
REA에 도움을 보태 주세요! [issue를 등록](https://github.com/morluto/rea/issues)해 버그나 기능을 제안하거나, [pull request를 보내](https://github.com/morluto/rea/pulls) 코드와 문서를 개선할 수 있습니다.
|
||||
|
||||
개발 환경과 검사 항목은 [CONTRIBUTING.md](CONTRIBUTING.md), 검증 절차는 [테스트 가이드](docs/testing.md), 프로젝트 구조는 [아키텍처 지도](docs/architecture.mermaid)를 참고하세요.
|
||||
|
||||
## 프로젝트 링크
|
||||
|
||||
[웹사이트](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [보안](SECURITY.md)
|
||||
|
||||
## 스타 기록
|
||||
|
||||
🎉 **GitHub 스타 30,000개, 감사합니다!**
|
||||
|
||||
REA를 사용하고, 버그를 보고하고, 기능을 요청하고, 빌드를 테스트하고, 수정에 기여해 주신 모든 분께 감사드립니다.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub 스타 기록" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 면책 조항
|
||||
|
||||
REA는 합법적인 리버스 엔지니어링 연구, 분석, 재구성을 위한 도구를 제공합니다. 필요한 권한을 얻고 관련 법률을 준수할 책임은 사용자에게 있습니다. 이 프로젝트는 불법적이거나 허가받지 않은 사용을 지지하지 않습니다.
|
||||
|
||||
## 라이선스
|
||||
|
||||
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · **Polski** · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
### Jeden serwer MCP do inżynierii wstecznej plików binarnych, aplikacji i zachowania w czasie wykonywania.
|
||||
|
||||
**Widzisz funkcję, która Ci się podoba? Dowiedz się, jak działa, aż do poziomu kodu binarnego.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Strona internetowa](https://rea.tools/) · [Przewodniki](https://rea.tools/guides/) · [Przykłady zastosowań](https://rea.tools/showcase/)**
|
||||
|
||||
[Szybki start](#szybki-start) · [Jak działa REA](#jak-działa-rea) · [Co możesz analizować](#co-możesz-analizować) · [Przykłady zastosowań](#przykłady-zastosowań) · [FAQ](#faq) · [Dokumentacja](#dokumentacja)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA uruchamia most analityczny w Hopperze podczas analizy natywnego pliku binarnego" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Społeczność REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Dołącz do społeczności inżynierii wstecznej</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Pytania i odpowiedzi · Pokaż swój projekt</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Widzisz w aplikacji funkcję, którą chcesz mieć we własnym produkcie? Poproś swojego agenta, aby zbadał ją za pomocą REA. Może przeanalizować aplikację bez jej kodu źródłowego, wyjaśnić działanie funkcji, pokazać dowody i stworzyć jej wersję dla Twojego projektu.
|
||||
|
||||
REA łączy Twojego agenta z narzędziami do analizy natywnych plików binarnych, aplikacji JavaScript i Electron, zestawów .NET oraz stron internetowych. Z tych samych narzędzi możesz korzystać w terminalu. Analiza odbywa się lokalnie, a wyniki zawierają dowody i ograniczenia stojące za każdym wnioskiem.
|
||||
|
||||
Konfigurator rejestruje REA w Twoim agencie i instaluje pasujące instrukcje pracy. Analiza kodu natywnego może korzystać z istniejącej instalacji Hoppera lub Ghidry; konfigurator może też zainstalować Hoppera po uzyskaniu zgody. Statyczna analiza JavaScript nie wymaga żadnego z tych silników.
|
||||
|
||||
> **[Odwiedź stronę REA](https://rea.tools/)**, aby znaleźć instrukcje konfiguracji, ilustrowane przewodniki i rzeczywiste studia przypadków.
|
||||
|
||||
## Szybki start
|
||||
|
||||
### Skonfiguruj agenta
|
||||
|
||||
Po zainstalowaniu Node.js i npm uruchom:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Wybierz swoich agentów, sprawdź proponowane zmiany i zatwierdź je. Konfigurator dodaje serwer MCP REA i pasujące instrukcje pracy, tworząc kopie zapasowe istniejącej konfiguracji. Następnie uruchom agenta ponownie.
|
||||
|
||||
Konfigurator obsługuje Claude Code, Codex, Cursor, Gemini CLI, Grok Build i [innych agentów](docs/installation.md#supported-agents). Konfigurację dostawców analizy i ręczną rejestrację MCP opisuje przewodnik [instalacji i konfiguracji](docs/installation.md).
|
||||
|
||||
### Zapytaj agenta
|
||||
|
||||
```text
|
||||
Sprawdź, jak działa wyszukiwanie w aplikacji Notes, pokaż mi dowody i stwórz podobną funkcję dla mojego projektu.
|
||||
```
|
||||
|
||||
Zastąp Notes wybraną aplikacją i wskaż funkcję, którą chcesz zrozumieć.
|
||||
|
||||
### Korzystaj z terminala
|
||||
|
||||
Przeanalizuj rozpakowany katalog aplikacji JavaScript/Electron lub archiwum ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Wynik obejmuje moduły, importy, granice komponentów Electron oraz dowody. Zastąp ścieżkę ścieżką do swojego celu, na przykład `"D:/apps/example"` w systemie Windows.
|
||||
|
||||
Aby zainstalować polecenie `rea` do regularnego użytku:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Przed analizą kodu natywnego skonfiguruj dostawcę analizy. Polecenia analizy natywnej, wybór dostawcy, migawki i użycie w skryptach opisuje [przewodnik CLI i Evidence](docs/cli.md).
|
||||
|
||||
### Zaktualizuj REA
|
||||
|
||||
REA szybko się rozwija, a nowe wydania często zawierają poprawki błędów. Dbaj o aktualność swojej instalacji.
|
||||
|
||||
Dla CLI zainstalowanego przez npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Aby odświeżyć rejestracje agentów i umiejętność, uruchom polecenie konfiguracji wyświetlone po aktualizacji.
|
||||
|
||||
Jeśli używasz `npx`, zaktualizuj konfigurację agenta poleceniem:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Sprawdź zmiany konfiguracji i uruchom agenta ponownie. Do jednorazowych poleceń CLI używaj `npx rea-agents@latest`, a następnie nazwy polecenia.
|
||||
|
||||
## Jak działa REA
|
||||
|
||||
Twój agent wywołuje REA przez MCP, aby analizować cel i śledzić istotny kod. REA zwraca ustalenia wraz z dowodami. Agent wykorzystuje je do zadawania dalszych pytań, wyjaśniania zachowania lub pisania i testowania implementacji. Polecenia CLI korzystają z tych samych procedur.
|
||||
|
||||

|
||||
|
||||
[Otwórz ilustrację w pełnym rozmiarze](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Co możesz analizować
|
||||
|
||||
REA wymaga Node.js 22.x (>=22.19), 24.x (>=24.11) lub 26+ oraz npm. Dodatkowe narzędzia i obsługiwane platformy zależą od celu analizy:
|
||||
|
||||
| Cel | Co zwraca REA | Wymagania i przewodnik |
|
||||
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Natywne pliki binarne | Pseudokod, kod asemblera, ciągi znaków, symbole, wywołania i odwołania | Hopper, Ghidra lub IDA; [analiza kodu natywnego](https://rea.tools/guides/native/) |
|
||||
| Układ pliku ELF w trybie offline | Sekcje, segmenty, oryginalne symbole/relokacje i potencjalne mechanizmy zabezpieczeń wskazane przez analizę statyczną | pwntools dostarczone przez użytkownika w systemie Linux x64; [diagnostyka plików binarnych](docs/binary-diagnostics.md) |
|
||||
| Kod bajtowy EVM | Selektory wywołań, przesunięcia bajtowe, wywnioskowane argumenty i mutowalność | Lokalny plik z surowymi bajtami lub zapisem szesnastkowym; [przewodnik EVM offline](docs/evm-bytecode.md) |
|
||||
| Zarejestrowane awarie systemu Linux | Surowe rekordy note, rejestry/sygnały każdego zarejestrowanego wątku i opcjonalni kandydaci na mapowania | pwntools dostarczone przez użytkownika; opcjonalnie GDB/pwndbg; [zarejestrowane awarie](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Moduły, importy, mapy źródeł, trasy, IPC i powiązania z natywnymi dodatkami | Node.js i npm; [analiza aplikacji](https://rea.tools/guides/javascript/) |
|
||||
| Strony internetowe | Struktura strony, skrypty, obserwacje sieciowe i zamówione zrzuty ekranu | Przeglądarka z rodziny Chrome; [analiza w przeglądarce](https://rea.tools/guides/browser/) |
|
||||
| Zapisane przechwycenia ruchu sieciowego | Żądania, odpowiedzi, dostępne dane przesyłane i lokalizacje źródłowe | HAR; mitmdump w systemie Linux dla natywnych przechwyceń mitmproxy; [przewodnik przechwytywania](docs/web-network-captures.md) |
|
||||
| Zestawy .NET | Metadane, instrukcje CIL, zadeklarowane zależności natywne i porównania kompilacji | Analiza statyczna; [przewodnik kodu zarządzanego](docs/managed-code-analysis.md) |
|
||||
| Pakiety APK dla Androida | Deklaracje manifestu, klasy, zdekompilowane metody i odwołania | JADX bez interfejsu graficznego i pełny JDK w systemie Linux/macOS; [przewodnik Androida](docs/android-analysis.md) |
|
||||
| Oprogramowanie układowe | Regiony, wyniki wyodrębniania i przekazanie do analizy kodu natywnego | Binwalk / Unblob w systemie Linux; [przewodnik oprogramowania układowego](docs/firmware-analysis.md) |
|
||||
| Pakiety i zasoby | Spisy plików, skróty, pliki plist, struktura pakietów Apple i wyodrębnione zasoby | [Przewodnik artefaktów i JavaScript](docs/javascript-artifact-reconstruction.md), [aplikacje Apple](docs/apple-application-analysis.md) |
|
||||
| Zachowanie procesów | Wyjście terminala, interakcje, obserwacje zakończenia i systemu plików oraz porównania uruchomień | Linux/macOS z natywnym PTY; [przechwytywanie procesów](docs/process-capture.md) |
|
||||
|
||||
Statyczna analiza JavaScript i .NET odczytuje dostarczone pliki bez uruchamiania aplikacji. Przechwytywanie w czasie wykonywania uruchamia wybrany cel lub wchodzi z nim w interakcje z uprawnieniami Twojego użytkownika; skutki opisano w odpowiednich przewodnikach.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Obsługiwane formaty natywne i platformy zależą od dostawcy analizy. Zobacz [konfigurację Hoppera i Ghidry](docs/installation.md#hopper), [przewodnik IDA](docs/ida-provider.md) oraz [eksperymentalną obsługę Ghidry w systemie Windows](docs/windows-ghidra-p0.md). Ghidra obsługuje też [analizę 16-bitowego DOS-a](docs/ghidra-dos.md). Wybór dostawcy opisuje [przewodnik CLI](docs/cli.md#choose-a-provider). Sprawdź [dostępność w wydaniach](docs/installation.md#released-package-and-main) dla funkcji dodanych po najnowszym wydaniu npm.
|
||||
|
||||
## Przykłady zastosowań
|
||||
|
||||
### DX-Ball: odtworzenie obliczania panoramy dźwięku
|
||||
|
||||
Prześledź wywołanie dźwięku do funkcji pomocniczej przeliczającej pozycję na panoramę, sprawdź instrukcje i przekształć niepełny pseudokod w C. Odtworzona implementacja przechodzi 3 205 przypadków dla oryginalnego x86 i odtwarza wszystkie 63 bajty skompilowanej funkcji.
|
||||
|
||||
[Przeczytaj studium przypadku](https://rea.tools/showcase/dx-ball/) · [Repozytorium odtworzonej implementacji](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: śledzenie mostu schowka w Electron
|
||||
|
||||
Znajdź API schowka w procesie renderującym, prześledź je przez preload i IPC do procesu głównego, a następnie sprawdź format schowka wraz z danymi formatowania.
|
||||
|
||||
[Przeczytaj studium przypadku](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: odtworzenie obliczania pierścienia pocisków w DOS-ie
|
||||
|
||||
Sprawdź 16-bitowe instrukcje oryginalnej gry na PC-98, odtwórz obliczenia kątów stałych i skierowanych na cel oraz porównaj odtworzony C++ z wynikiem historycznego kompilatora.
|
||||
|
||||
[Przeczytaj studium przypadku](https://rea.tools/showcase/th04/) · [Repozytorium odtworzonej implementacji](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Jeśli użyłeś REA do czegoś ciekawego, chętnie to zobaczymy. Opisz swój przypadek w [zgłoszeniu](https://github.com/morluto/rea/issues) lub [pull requeście](https://github.com/morluto/rea/pulls), podając cel analizy, pytanie, sposób, w jaki REA pomogło, i swoje ustalenia.
|
||||
|
||||
## FAQ
|
||||
|
||||
<details>
|
||||
<summary><strong>Którzy agenci mogą korzystać z REA?</strong></summary>
|
||||
|
||||
Każdy agent obsługujący lokalne serwery MCP. Konfigurator konfiguruje [obsługiwanych agentów](docs/installation.md#supported-agents); inne klienty mogą użyć [ręcznej rejestracji MCP](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Czy potrzebuję Hoppera, Ghidry lub IDA?</strong></summary>
|
||||
|
||||
Dogłębna analiza kodu natywnego używa jednego z nich. Statyczna analiza JavaScript i .NET działa bez silnika analizy natywnej. Konfigurator może zainstalować Hoppera po uzyskaniu zgody; Ghidra i IDA korzystają z istniejących instalacji. Zobacz [konfigurację dostawców analizy](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Czy muszę najpierw uruchomić Hoppera?</strong></summary>
|
||||
|
||||
REA uruchamia Hoppera, gdy wymaga tego operacja. W systemie macOS przy pierwszym uruchomieniu okno dialogowe może poprosić o wybór trybu demonstracyjnego lub aktywację licencji. Zobacz [uruchamianie Hoppera i rozwiązywanie problemów](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Co daje zainstalowanie umiejętności z skills.sh?</strong></summary>
|
||||
|
||||
Umiejętność dostarcza agentowi instrukcje prowadzenia analizy. Użyj `rea setup`, aby zarejestrować serwer MCP REA i zainstalować pasujące instrukcje, a następnie uruchom agenta ponownie. Zobacz [instalację samej umiejętności](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Jaki kod zwraca REA?</strong></summary>
|
||||
|
||||
Analiza kodu natywnego zwraca pseudokod i kod asemblera. Analiza JavaScript/Electron odzyskuje moduły i ich powiązania. Agent wykorzystuje te ustalenia do napisania i przetestowania implementacji; [przykłady zastosowań](#przykłady-zastosowań) pokazują ten proces w praktyce.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Czy REA przesyła moją aplikację?</strong></summary>
|
||||
|
||||
REA analizuje cele lokalnie. Twój agent otrzymuje wyniki narzędzi, a dostawca jego modelu ma własną politykę dotyczącą danych.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Co zrobić, gdy napotkam błąd?</strong></summary>
|
||||
|
||||
Najpierw zaktualizuj REA; najnowsze wydanie mogło już naprawić ten problem.
|
||||
|
||||
Dla CLI zainstalowanego przez npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Dla konfiguracji agenta przez `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Jeśli używasz agenta, wykonaj [odświeżenie konfiguracji](#zaktualizuj-rea) i uruchom go ponownie. Ponów tę samą operację. Jeśli problem pozostaje, [otwórz zgłoszenie](https://github.com/morluto/rea/issues), podając wersję REA, typ celu analizy, kroki odtworzenia problemu i komunikaty błędu.
|
||||
|
||||
</details>
|
||||
|
||||
## Dokumentacja
|
||||
|
||||
Zacznij od [przewodników z przykładami](https://rea.tools/guides/) na stronie internetowej. Dokładne opcje, wymagania wstępne i kontrakty wyników opisano tutaj:
|
||||
|
||||
- [Instalacja i konfiguracja](docs/installation.md): rejestracja agentów, konfiguracja dostawców analizy, aktualizacje i odinstalowanie.
|
||||
- [Gotowość i rozwiązywanie problemów](docs/installation.md#check-readiness-for-your-task): diagnostyka jednego agenta lub silnika analizy.
|
||||
- [CLI i Evidence](docs/cli.md): polecenia, wybór dostawcy, migawki, import/eksport i kody zakończenia.
|
||||
- [Kontrakty MCP](docs/mcp-contracts.md) i [prompty dla agentów](docs/mcp-prompts.md): wyniki narzędzi, sesje i prowadzone analizy.
|
||||
- [Katalog narzędzi](docs/mcp-contracts.md#generated-catalog): generowany podczas kompilacji wykaz narzędzi, dostawców i poleceń CLI.
|
||||
- [Plan rozwoju](docs/roadmap.md): planowane prace i śledzenie funkcjonalności.
|
||||
|
||||
Zgłaszaj podatności zgodnie z [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Współtworzenie
|
||||
|
||||
Chętnie przyjmiemy Twoją pomoc w rozwoju REA! [Otwórz zgłoszenie](https://github.com/morluto/rea/issues), aby poinformować o błędzie lub zaproponować funkcję, albo [wyślij pull request](https://github.com/morluto/rea/pulls) z ulepszeniami kodu lub dokumentacji.
|
||||
|
||||
Zobacz [CONTRIBUTING.md](CONTRIBUTING.md), aby poznać konfigurację środowiska programistycznego i kontrole, [testowanie](docs/testing.md), aby poznać ścieżki weryfikacji, oraz [mapę architektury](docs/architecture.mermaid), aby poznać strukturę projektu.
|
||||
|
||||
## Linki projektu
|
||||
|
||||
[Strona internetowa](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Zgłoszenia](https://github.com/morluto/rea/issues) · [Bezpieczeństwo](SECURITY.md)
|
||||
|
||||
## Historia gwiazdek
|
||||
|
||||
🎉 **30 000 gwiazdek na GitHubie — dziękujemy!**
|
||||
|
||||
Dziękujemy wszystkim, którzy używają REA, zgłaszają błędy, proponują nowe funkcje, testują kompilacje i przesyłają poprawki.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Historia gwiazdek REA na GitHubie" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Zastrzeżenie
|
||||
|
||||
REA dostarcza narzędzia do zgodnych z prawem badań z zakresu inżynierii wstecznej, analizy i odtwarzania. Odpowiadasz za uzyskanie wymaganych zezwoleń i przestrzeganie obowiązującego prawa. Projekt nie popiera działań nielegalnych ani nieautoryzowanych.
|
||||
|
||||
## Licencja
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · **Português (Brasil)** · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Faça engenharia reversa de qualquer coisa
|
||||
|
||||
### Um único MCP para engenharia reversa de binários, aplicativos e comportamento em execução.
|
||||
|
||||
**Viu uma funcionalidade de que gostou? Entenda como ela funciona, até o nível binário.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Site](https://rea.tools/) · [Guias](https://rea.tools/guides/) · [Casos de uso](https://rea.tools/showcase/)**
|
||||
|
||||
[Início rápido](#início-rápido) · [Como o REA funciona](#como-o-rea-funciona) · [O que você pode analisar](#o-que-você-pode-analisar) · [Casos de uso](#casos-de-uso) · [Perguntas frequentes](#perguntas-frequentes) · [Documentação](#documentação)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA inicia sua ponte de análise dentro do Hopper enquanto inspeciona um binário nativo" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Comunidade REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Participe da comunidade de engenharia reversa</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Perguntas e respostas · Compartilhe seus resultados</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Viu uma funcionalidade em um aplicativo que gostaria de ter no seu próprio produto? Peça ao seu agente para investigá-la com o REA. Ele pode inspecionar o aplicativo sem o código-fonte, explicar como a funcionalidade funciona, apresentar as evidências e criar uma versão para o seu projeto.
|
||||
|
||||
O REA conecta seu agente a ferramentas para inspecionar binários nativos, aplicativos JavaScript e Electron, assemblies .NET e sites. Você também pode usar as mesmas ferramentas pelo terminal. A análise acontece localmente, e os resultados incluem as evidências e limitações por trás de cada conclusão.
|
||||
|
||||
A configuração registra o REA no seu agente e instala as instruções de fluxo de trabalho correspondentes. A análise nativa pode usar uma instalação existente do Hopper ou do Ghidra; a configuração também pode instalar o Hopper opcionalmente, mediante aprovação. A análise estática de JavaScript não precisa de nenhum dos dois motores.
|
||||
|
||||
> **[Visite o site do REA](https://rea.tools/)** para instruções de configuração, guias ilustrados e estudos de caso reais.
|
||||
|
||||
## Início rápido
|
||||
|
||||
### Configure seu agente
|
||||
|
||||
Com Node.js e npm instalados, execute:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Escolha seus agentes, revise as alterações propostas e aprove-as. A configuração adiciona o servidor MCP do REA e as instruções de fluxo de trabalho correspondentes, com backups das configurações existentes. Reinicie seu agente depois.
|
||||
|
||||
A configuração oferece suporte ao Claude Code, Codex, Cursor, Gemini CLI, Grok Build e [outros agentes](docs/installation.md#supported-agents). Consulte [instalação e configuração](docs/installation.md) para configurar provedores e registrar o MCP manualmente.
|
||||
|
||||
### Peça ao seu agente
|
||||
|
||||
```text
|
||||
Investigue como a busca funciona no aplicativo Notes, mostre as evidências e crie uma funcionalidade semelhante para o meu projeto.
|
||||
```
|
||||
|
||||
Substitua Notes pelo aplicativo alvo e indique a funcionalidade que deseja entender.
|
||||
|
||||
### Use o terminal
|
||||
|
||||
Inspecione um diretório extraído de um aplicativo JavaScript/Electron ou um arquivo ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
O resultado inclui módulos, importações, limites do Electron e suas evidências. Substitua o caminho pelo do seu alvo, como `"D:/apps/example"` no Windows.
|
||||
|
||||
Para instalar o comando `rea` e usá-lo regularmente:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Para análise nativa, configure primeiro um provedor. Consulte o [guia de CLI e Evidence](docs/cli.md) para comandos nativos, seleção de provedores, snapshots e uso em scripts.
|
||||
|
||||
### Atualize o REA
|
||||
|
||||
O REA muda rapidamente, e novas versões incluem correções frequentes de bugs. Mantenha sua instalação atualizada.
|
||||
|
||||
Para uma CLI instalada pelo npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Para atualizar os registros dos agentes e a skill, execute o comando de configuração exibido pela atualização.
|
||||
|
||||
Se você usa `npx`, atualize a configuração do agente com:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Revise as alterações de configuração e reinicie seu agente. Para comandos avulsos da CLI, use `npx rea-agents@latest` seguido do comando.
|
||||
|
||||
## Como o REA funciona
|
||||
|
||||
Seu agente chama o REA por MCP para inspecionar o alvo e rastrear o código relevante. O REA retorna os resultados com suas evidências. O agente usa esses resultados para fazer novas perguntas, explicar o comportamento ou escrever e testar uma implementação. Os comandos da CLI usam os mesmos fluxos de trabalho.
|
||||
|
||||

|
||||
|
||||
[Abra a figura em tamanho completo](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## O que você pode analisar
|
||||
|
||||
O REA requer Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, além do npm. As ferramentas adicionais e os sistemas anfitriões compatíveis dependem do alvo:
|
||||
|
||||
| Alvo | O que o REA retorna | Requisitos e guia |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binários nativos | Pseudocódigo, assembly, strings, símbolos, chamadas e referências | Hopper, Ghidra ou IDA; [análise nativa](https://rea.tools/guides/native/) |
|
||||
| Estrutura ELF sem execução | Seções, segmentos, símbolos/relocações originais e possíveis mecanismos de mitigação identificados por análise estática | pwntools fornecido pelo chamador no Linux x64; [diagnóstico de binários](docs/binary-diagnostics.md) |
|
||||
| Bytecode EVM | Seletores de despacho, offsets de bytes, argumentos inferidos e mutabilidade | Entrada local com bytes brutos ou representação hexadecimal; [guia de EVM sem execução](docs/evm-bytecode.md) |
|
||||
| Falhas do Linux registradas | Registros note brutos, registradores/sinais de cada thread registrada e candidatos opcionais a mapeamentos | pwntools fornecido pelo chamador; GDB/pwndbg opcionais; [falhas registradas](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Módulos, importações, source maps, rotas, IPC e relações com complementos nativos | Node.js e npm; [análise de aplicativos](https://rea.tools/guides/javascript/) |
|
||||
| Sites | Estrutura da página, scripts, observações de rede e capturas de tela solicitadas | Um navegador da família Chrome; [análise do navegador](https://rea.tools/guides/browser/) |
|
||||
| Capturas de rede salvas | Requisições, respostas, payloads acessíveis e localizações de origem | HAR; mitmdump no Linux para capturas no formato nativo do mitmproxy; [guia de capturas](docs/web-network-captures.md) |
|
||||
| Assemblies .NET | Metadados, instruções CIL, dependências nativas declaradas e comparações de builds | Inspeção estática; [guia de código gerenciado](docs/managed-code-analysis.md) |
|
||||
| APKs Android | Declarações do manifesto, classes, métodos descompilados e referências | JADX sem interface gráfica e um JDK completo no Linux/macOS; [guia de Android](docs/android-analysis.md) |
|
||||
| Firmware | Regiões, resultados de extração e encaminhamentos à análise nativa | Binwalk / Unblob no Linux; [guia de firmware](docs/firmware-analysis.md) |
|
||||
| Pacotes e recursos | Inventários de arquivos, digests, plists, estrutura de bundles Apple e recursos extraídos | [Guia de artefatos e JavaScript](docs/javascript-artifact-reconstruction.md), [aplicativos Apple](docs/apple-application-analysis.md) |
|
||||
| Comportamento de processos | Saída do terminal, interações, observações de encerramento e do sistema de arquivos, e comparações entre execuções | Linux/macOS com PTY nativo; [captura de processos](docs/process-capture.md) |
|
||||
|
||||
A inspeção estática de JavaScript e .NET lê os arquivos fornecidos sem executar o aplicativo. A captura em execução inicia o alvo selecionado ou interage com ele usando as permissões do seu usuário; cada guia descreve seus efeitos.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Os formatos nativos e os sistemas anfitriões compatíveis variam por provedor. Consulte [configuração do Hopper e Ghidra](docs/installation.md#hopper), o [guia do IDA](docs/ida-provider.md) e o [suporte experimental ao Ghidra no Windows](docs/windows-ghidra-p0.md). O Ghidra também oferece [análise de DOS de 16 bits](docs/ghidra-dos.md). Para selecionar um provedor, consulte o [guia da CLI](docs/cli.md#choose-a-provider). Verifique a [disponibilidade nas versões](docs/installation.md#released-package-and-main) das funcionalidades adicionadas após a versão mais recente no npm.
|
||||
|
||||
## Casos de uso
|
||||
|
||||
### DX-Ball: reconstruir um cálculo de panorâmica de áudio
|
||||
|
||||
Siga uma chamada de áudio até a função auxiliar que converte posição em panorâmica, inspecione as instruções e transforme pseudocódigo incompleto em C. A reconstrução passa em 3.205 casos com o x86 original e reproduz todos os 63 bytes da função compilada.
|
||||
|
||||
[Leia o estudo de caso](https://rea.tools/showcase/dx-ball/) · [Repositório da reconstrução](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: rastrear a ponte da área de transferência do Electron
|
||||
|
||||
Encontre a API da área de transferência do renderizador, siga-a por preload e IPC até o processo principal e inspecione o formato da área de transferência com seus dados de formatação.
|
||||
|
||||
[Leia o estudo de caso](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: recuperar o cálculo de um anel de projéteis no DOS
|
||||
|
||||
Inspecione as instruções de 16 bits do jogo original para PC-98, recupere os cálculos de ângulos fixos e direcionados e compare o C++ reconstruído com a saída do compilador da época.
|
||||
|
||||
[Leia o estudo de caso](https://rea.tools/showcase/th04/) · [Repositório da reconstrução](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Se você usou o REA em algo interessante, gostaríamos de conhecer. Compartilhe seu caso em uma [issue](https://github.com/morluto/rea/issues) ou um [pull request](https://github.com/morluto/rea/pulls), incluindo o alvo, sua pergunta, como o REA ajudou e o que descobriu.
|
||||
|
||||
## Perguntas frequentes
|
||||
|
||||
<details>
|
||||
<summary><strong>Quais agentes podem usar o REA?</strong></summary>
|
||||
|
||||
Qualquer agente que ofereça suporte a servidores MCP locais. A configuração prepara os [agentes compatíveis](docs/installation.md#supported-agents); outros clientes podem usar o [registro manual do MCP](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Preciso do Hopper, Ghidra ou IDA?</strong></summary>
|
||||
|
||||
A análise nativa aprofundada usa um desses motores. A inspeção estática de JavaScript e .NET funciona sem um motor de análise nativa. A configuração pode instalar o Hopper após aprovação; Ghidra e IDA usam as instalações existentes. Consulte [configuração dos provedores](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Preciso iniciar o Hopper antes?</strong></summary>
|
||||
|
||||
O REA inicia o Hopper quando uma operação precisa dele. No macOS, um diálogo na primeira execução pode pedir que você escolha o modo de demonstração ou ative a licença. Consulte [inicialização do Hopper e solução de problemas](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>O que faz a instalação da skill pelo skills.sh?</strong></summary>
|
||||
|
||||
A skill fornece instruções de investigação ao seu agente. Use `rea setup` para registrar o servidor MCP do REA e instalar as instruções correspondentes, depois reinicie seu agente. Consulte [instalação apenas da skill](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Que código o REA retorna?</strong></summary>
|
||||
|
||||
A análise nativa retorna pseudocódigo e assembly. A análise de JavaScript/Electron recupera módulos e suas relações. Seu agente usa esses resultados para escrever e testar uma implementação; os [casos de uso](#casos-de-uso) apresentam exemplos completos.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>O REA envia meu aplicativo para algum servidor?</strong></summary>
|
||||
|
||||
O REA analisa os alvos localmente. Seu agente recebe os resultados das ferramentas, e o provedor do modelo tem sua própria política de dados.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>O que devo fazer se encontrar um bug?</strong></summary>
|
||||
|
||||
Atualize primeiro; uma versão recente pode já ter corrigido o problema.
|
||||
|
||||
Para uma CLI instalada pelo npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Para configurar o agente por `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Se você usa um agente, conclua a [atualização da configuração](#atualize-o-rea) e reinicie-o. Tente executar a mesma tarefa novamente. Se o problema persistir, [abra uma issue](https://github.com/morluto/rea/issues) com sua versão do REA, o tipo de alvo, os passos para reproduzir e a saída do erro.
|
||||
|
||||
</details>
|
||||
|
||||
## Documentação
|
||||
|
||||
Comece pelos [guias práticos](https://rea.tools/guides/) do site. Para opções exatas, pré-requisitos e contratos dos resultados:
|
||||
|
||||
- [Instalação e configuração](docs/installation.md): registro de agentes, configuração de provedores, atualizações e desinstalação.
|
||||
- [Prontidão e solução de problemas](docs/installation.md#check-readiness-for-your-task): diagnóstico de um agente ou motor de análise específico.
|
||||
- [CLI e Evidence](docs/cli.md): comandos, seleção de provedores, snapshots, importação/exportação e status de saída.
|
||||
- [Contratos MCP](docs/mcp-contracts.md) e [prompts para agentes](docs/mcp-prompts.md): resultados das ferramentas, sessões e investigações guiadas.
|
||||
- [Catálogo de ferramentas](docs/mcp-contracts.md#generated-catalog): inventário de ferramentas, provedores e comandos da CLI gerado durante o build.
|
||||
- [Roadmap](docs/roadmap.md): trabalho planejado e acompanhamento das capacidades.
|
||||
|
||||
Relate vulnerabilidades conforme [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contribuir
|
||||
|
||||
Sua ajuda com o REA é bem-vinda! [Abra uma issue](https://github.com/morluto/rea/issues) para relatar um bug ou sugerir uma funcionalidade, ou [envie um pull request](https://github.com/morluto/rea/pulls) para melhorar o código ou a documentação.
|
||||
|
||||
Consulte [CONTRIBUTING.md](CONTRIBUTING.md) para configurar o ambiente de desenvolvimento e as verificações, o [guia de testes](docs/testing.md) para os fluxos de verificação e o [mapa da arquitetura](docs/architecture.mermaid) para a estrutura do projeto.
|
||||
|
||||
## Links do projeto
|
||||
|
||||
[Site](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Segurança](SECURITY.md)
|
||||
|
||||
## Histórico de estrelas
|
||||
|
||||
🎉 **30.000 estrelas no GitHub — muito obrigado!**
|
||||
|
||||
Obrigado a todos que usam o REA, relatam bugs, sugerem funcionalidades, testam builds e contribuem com correções.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Histórico de estrelas do REA no GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Aviso legal
|
||||
|
||||
O REA fornece ferramentas para pesquisa, análise e reconstrução por engenharia reversa dentro da lei. Você é responsável por obter as autorizações necessárias e cumprir as leis aplicáveis. O projeto não apoia usos ilegais ou não autorizados.
|
||||
|
||||
## Licença
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · **Русский** · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Реверс-инжиниринг чего угодно
|
||||
|
||||
### Один MCP для реверс-инжиниринга бинарных файлов, приложений и поведения во время выполнения.
|
||||
|
||||
**Нашли интересную функцию? Разберитесь, как она работает, вплоть до двоичного кода.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Сайт](https://rea.tools/) · [Руководства](https://rea.tools/guides/) · [Примеры](https://rea.tools/showcase/)**
|
||||
|
||||
[Быстрый старт](#быстрый-старт) · [Как работает REA](#как-работает-rea) · [Что можно анализировать](#что-можно-анализировать) · [Примеры](#примеры) · [Частые вопросы](#частые-вопросы) · [Документация](#документация)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA запускает мост анализа внутри Hopper при исследовании нативного бинарного файла" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Сообщество REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Присоединяйтесь к сообществу реверс-инжиниринга</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Вопросы и ответы · Обмен результатами</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Увидели в приложении функцию, которую хотите добавить в свой продукт? Попросите агента исследовать её с помощью REA. Он может изучить приложение без исходного кода, объяснить работу функции, показать подтверждающие данные и создать подходящую реализацию для вашего проекта.
|
||||
|
||||
REA подключает вашего агента к инструментам для исследования нативных бинарных файлов, приложений JavaScript и Electron, сборок .NET и сайтов. Те же инструменты доступны в терминале. Анализ выполняется локально, а результаты содержат данные и ограничения, на которых основан каждый вывод.
|
||||
|
||||
Настройка регистрирует REA в вашем агенте и устанавливает соответствующие инструкции по работе. Для нативного анализа можно использовать уже установленный Hopper или Ghidra; настройка также может установить Hopper с вашего согласия. Для статического анализа JavaScript не нужен ни один из этих движков.
|
||||
|
||||
> **[Посетите сайт REA](https://rea.tools/)**: там есть инструкции по настройке, иллюстрированные руководства и реальные исследования.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### Настройте агента
|
||||
|
||||
Если Node.js и npm установлены, выполните:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Выберите агентов, проверьте предлагаемые изменения и подтвердите их. Настройка добавляет MCP-сервер REA и соответствующие рабочие инструкции, сохраняя резервные копии существующих настроек. После этого перезапустите агента.
|
||||
|
||||
Настройка поддерживает Claude Code, Codex, Cursor, Gemini CLI, Grok Build и [других агентов](docs/installation.md#supported-agents). Настройка провайдеров и ручная регистрация MCP описаны в разделе [установка и настройка](docs/installation.md).
|
||||
|
||||
### Задайте вопрос агенту
|
||||
|
||||
```text
|
||||
Исследуй, как работает поиск в приложении Notes, покажи подтверждающие данные и создай похожую функцию для моего проекта.
|
||||
```
|
||||
|
||||
Замените Notes нужным приложением и укажите функцию, которую хотите понять.
|
||||
|
||||
### Используйте терминал
|
||||
|
||||
Исследуйте распакованный каталог приложения JavaScript/Electron или файл ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Результат включает модули, импорты, границы Electron и подтверждающие данные. Замените путь своим, например `"D:/apps/example"` в Windows.
|
||||
|
||||
Для регулярного использования установите команду `rea`:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Для нативного анализа сначала настройте провайдер. Нативные команды, выбор провайдера, снимки и работу со скриптами описывает [руководство по CLI и Evidence](docs/cli.md).
|
||||
|
||||
### Обновите REA
|
||||
|
||||
REA быстро развивается, и новые версии часто содержат исправления ошибок. Регулярно обновляйте установленную версию.
|
||||
|
||||
Для CLI, установленного через npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Чтобы обновить регистрации агентов и skill, выполните команду настройки, которую выведет обновление.
|
||||
|
||||
Если используете `npx`, обновите настройку агента командой:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Проверьте изменения настроек и перезапустите агента. Для разовых команд CLI используйте `npx rea-agents@latest`, а затем нужную команду.
|
||||
|
||||
## Как работает REA
|
||||
|
||||
Агент вызывает REA через MCP, чтобы исследовать цель и проследить связанный код. REA возвращает результаты вместе с подтверждающими данными. Агент использует их для уточняющих вопросов, объяснения поведения или написания и тестирования реализации. Команды CLI используют те же рабочие процессы.
|
||||
|
||||

|
||||
|
||||
[Открыть рисунок в полном размере](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Что можно анализировать
|
||||
|
||||
REA требует Node.js 22.x (>=22.19), 24.x (>=24.11) или 26+, а также npm. Дополнительные инструменты и поддерживаемые системы зависят от цели:
|
||||
|
||||
| Цель | Что возвращает REA | Требования и руководство |
|
||||
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Нативные бинарные файлы | Псевдокод, ассемблер, строки, символы, вызовы и ссылки | Hopper, Ghidra или IDA; [нативный анализ](https://rea.tools/guides/native/) |
|
||||
| Структура ELF без запуска | Секции, сегменты, исходные символы/релокации и возможные механизмы защиты по данным статического анализа | pwntools, предоставленный вызывающей стороной, в Linux x64; [диагностика бинарных файлов](docs/binary-diagnostics.md) |
|
||||
| Байт-код EVM | Селекторы диспетчеризации, смещения байтов, предполагаемые аргументы и изменяемость состояния | Локальные необработанные байты или шестнадцатеричное представление; [руководство по EVM без запуска](docs/evm-bytecode.md) |
|
||||
| Записанные сбои Linux | Необработанные записи note, регистры/сигналы каждого записанного потока и необязательные кандидаты сопоставления с памятью | pwntools, предоставленный вызывающей стороной; необязательные GDB/pwndbg; [записанные сбои](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Модули, импорты, карты исходного кода, маршруты, IPC и связи с нативными дополнениями | Node.js и npm; [анализ приложений](https://rea.tools/guides/javascript/) |
|
||||
| Сайты | Структура страницы, скрипты, наблюдения за сетью и запрошенные снимки экрана | Браузер семейства Chrome; [анализ браузера](https://rea.tools/guides/browser/) |
|
||||
| Сохранённые сетевые захваты | Запросы, ответы, доступные полезные данные и позиции в источнике | HAR; mitmdump в Linux для захватов в собственном формате mitmproxy; [руководство по сетевым захватам](docs/web-network-captures.md) |
|
||||
| Сборки .NET | Метаданные, инструкции CIL, объявленные нативные зависимости и сравнения сборок | Статическое исследование; [руководство по управляемому коду](docs/managed-code-analysis.md) |
|
||||
| Android APK | Объявления манифеста, классы, декомпилированные методы и ссылки | JADX без графического интерфейса и полный JDK в Linux/macOS; [руководство по Android](docs/android-analysis.md) |
|
||||
| Прошивки | Области, результаты извлечения и передача в нативный анализ | Binwalk / Unblob в Linux; [руководство по прошивкам](docs/firmware-analysis.md) |
|
||||
| Пакеты и ресурсы | Списки файлов, хеши, plist, структура пакетов Apple и извлечённые ресурсы | [Руководство по артефактам и JavaScript](docs/javascript-artifact-reconstruction.md), [приложения Apple](docs/apple-application-analysis.md) |
|
||||
| Поведение процессов | Вывод терминала, взаимодействия, наблюдения за завершением и файловой системой, сравнения запусков | Linux/macOS с нативным PTY; [захват процессов](docs/process-capture.md) |
|
||||
|
||||
Статическое исследование JavaScript и .NET читает предоставленные файлы, не запуская приложение. Захват во время выполнения запускает выбранную цель или взаимодействует с ней с правами вашего пользователя; соответствующие руководства описывают последствия.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Нативные форматы и поддерживаемые системы различаются у разных провайдеров. См. [настройку Hopper и Ghidra](docs/installation.md#hopper), [руководство по IDA](docs/ida-provider.md) и [экспериментальную поддержку Ghidra в Windows](docs/windows-ghidra-p0.md). Ghidra также поддерживает [анализ 16-битного DOS](docs/ghidra-dos.md). Выбор провайдера описан в [руководстве по CLI](docs/cli.md#choose-a-provider). Для функций, добавленных после последней версии в npm, проверьте [доступность в релизах](docs/installation.md#released-package-and-main).
|
||||
|
||||
## Примеры
|
||||
|
||||
### DX-Ball: восстановление расчёта звуковой панорамы
|
||||
|
||||
Проследите звуковой вызов до функции, преобразующей координату в значение панорамы, изучите инструкции и превратите неполный псевдокод в C. Восстановленная реализация проходит 3 205 тестов с исходным x86-кодом и воспроизводит все 63 байта скомпилированной функции.
|
||||
|
||||
[Прочитать исследование](https://rea.tools/showcase/dx-ball/) · [Репозиторий реконструкции](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: исследование моста буфера обмена Electron
|
||||
|
||||
Найдите API буфера обмена в процессе рендеринга, проследите его через preload и IPC до основного процесса и изучите формат буфера обмена с форматированием.
|
||||
|
||||
[Прочитать исследование](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: восстановление расчёта кольца пуль в DOS
|
||||
|
||||
Изучите 16-битные инструкции оригинальной игры для PC-98, восстановите расчёты фиксированных и прицельных углов и сравните восстановленный C++ с выводом компилятора того времени.
|
||||
|
||||
[Прочитать исследование](https://rea.tools/showcase/th04/) · [Репозиторий реконструкции](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Если вы исследовали с REA что-то интересное, поделитесь результатом. Создайте [issue](https://github.com/morluto/rea/issues) или [pull request](https://github.com/morluto/rea/pulls) и укажите цель, ваш вопрос, как помог REA и что удалось узнать.
|
||||
|
||||
## Частые вопросы
|
||||
|
||||
<details>
|
||||
<summary><strong>Какие агенты могут использовать REA?</strong></summary>
|
||||
|
||||
Любой агент с поддержкой локальных MCP-серверов. Настройка конфигурирует [поддерживаемых агентов](docs/installation.md#supported-agents); другие клиенты могут использовать [ручную регистрацию MCP](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Нужны ли мне Hopper, Ghidra или IDA?</strong></summary>
|
||||
|
||||
Углублённый нативный анализ использует один из этих инструментов. Статическое исследование JavaScript и .NET работает без движка нативного анализа. Настройка может установить Hopper после подтверждения; Ghidra и IDA используют существующие установки. См. [настройку провайдеров](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Нужно ли сначала запускать Hopper?</strong></summary>
|
||||
|
||||
REA запускает Hopper, когда он нужен для операции. В macOS при первом запуске может появиться диалог выбора деморежима или активации лицензии. См. [запуск Hopper и устранение неполадок](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Что даёт установка skill с skills.sh?</strong></summary>
|
||||
|
||||
Skill предоставляет агенту инструкции по исследованию. Используйте `rea setup`, чтобы зарегистрировать MCP-сервер REA и установить соответствующие инструкции, затем перезапустите агента. См. [установку только skill](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Какой код возвращает REA?</strong></summary>
|
||||
|
||||
Нативный анализ возвращает псевдокод и ассемблер. Анализ JavaScript/Electron восстанавливает модули и их связи. Агент использует эти результаты, чтобы написать и протестировать реализацию; в [примерах](#примеры) показаны практические исследования.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA загружает моё приложение на сервер?</strong></summary>
|
||||
|
||||
REA анализирует цели локально. Ваш агент получает результаты инструментов, а у его поставщика моделей есть собственная политика обработки данных.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Что делать, если я столкнулся с ошибкой?</strong></summary>
|
||||
|
||||
Сначала обновитесь: недавний релиз мог уже исправить ошибку.
|
||||
|
||||
Для CLI, установленного через npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Для настройки агента через `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Если используете агента, завершите [обновление настройки](#обновите-rea) и перезапустите его. Повторите ту же задачу. Если проблема сохраняется, [откройте issue](https://github.com/morluto/rea/issues), указав версию REA, тип цели, шаги воспроизведения и вывод ошибки.
|
||||
|
||||
</details>
|
||||
|
||||
## Документация
|
||||
|
||||
Начните с [практических руководств](https://rea.tools/guides/) на сайте. Точные параметры, требования и контракты результатов описаны здесь:
|
||||
|
||||
- [Установка и настройка](docs/installation.md): регистрация агентов, настройка провайдеров, обновление и удаление.
|
||||
- [Готовность и устранение неполадок](docs/installation.md#check-readiness-for-your-task): диагностика конкретного агента или движка анализа.
|
||||
- [CLI и Evidence](docs/cli.md): команды, выбор провайдера, снимки, импорт/экспорт и коды завершения.
|
||||
- [Контракты MCP](docs/mcp-contracts.md) и [промпты агентов](docs/mcp-prompts.md): результаты инструментов, сеансы и исследования с подсказками.
|
||||
- [Каталог инструментов](docs/mcp-contracts.md#generated-catalog): создаваемый при сборке перечень инструментов, провайдеров и команд CLI.
|
||||
- [План развития](docs/roadmap.md): запланированные работы и отслеживание возможностей.
|
||||
|
||||
Сообщайте об уязвимостях согласно [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Участие в проекте
|
||||
|
||||
Будем рады вашей помощи с REA! [Откройте issue](https://github.com/morluto/rea/issues), чтобы сообщить об ошибке или предложить функцию, либо [отправьте pull request](https://github.com/morluto/rea/pulls) с улучшениями кода или документации.
|
||||
|
||||
Настройка среды разработки и проверки описаны в [CONTRIBUTING.md](CONTRIBUTING.md), направления проверки — в [руководстве по тестированию](docs/testing.md), а структура проекта — на [схеме архитектуры](docs/architecture.mermaid).
|
||||
|
||||
## Ссылки проекта
|
||||
|
||||
[Сайт](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Безопасность](SECURITY.md)
|
||||
|
||||
## История звёзд
|
||||
|
||||
🎉 **30 000 звёзд на GitHub — спасибо!**
|
||||
|
||||
Спасибо всем, кто использует REA, сообщает об ошибках, предлагает новые функции, тестирует сборки и помогает с исправлениями.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="История звёзд REA на GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Отказ от ответственности
|
||||
|
||||
REA предоставляет инструменты для законных исследований, анализа и реконструкции с помощью реверс-инжиниринга. Вы отвечаете за получение необходимых разрешений и соблюдение применимых законов. Проект не поддерживает незаконное или несанкционированное использование.
|
||||
|
||||
## Лицензия
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · **ไทย** · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
### MCP เดียวสำหรับวิศวกรรมย้อนกลับ ทั้งไบนารี แอปพลิเคชัน และพฤติกรรมขณะทำงาน
|
||||
|
||||
**เห็นฟีเจอร์ที่ชอบแล้วอยากรู้ว่าทำงานอย่างไร? เข้าใจได้ลึกถึงระดับไบนารี**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[เว็บไซต์](https://rea.tools/) · [คู่มือ](https://rea.tools/guides/) · [ตัวอย่างการใช้งาน](https://rea.tools/showcase/)**
|
||||
|
||||
[เริ่มต้นอย่างรวดเร็ว](#เริ่มต้นอย่างรวดเร็ว) · [REA ทำงานอย่างไร](#rea-ทำงานอย่างไร) · [สิ่งที่วิเคราะห์ได้](#สิ่งที่วิเคราะห์ได้) · [ตัวอย่างการใช้งาน](#ตัวอย่างการใช้งาน) · [คำถามที่พบบ่อย](#คำถามที่พบบ่อย) · [เอกสาร](#เอกสาร)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA เริ่มบริดจ์การวิเคราะห์ภายใน Hopper ขณะตรวจสอบไบนารีเนทีฟ" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="ชุมชน REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>เข้าร่วมชุมชนวิศวกรรมย้อนกลับ</strong>
|
||||
</a><br />
|
||||
<sub>Discord · ถามตอบ · แบ่งปันผลงาน</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
เห็นฟีเจอร์ในแอปที่อยากนำมาใช้ในผลิตภัณฑ์ของคุณเองหรือไม่? ขอให้เอเจนต์ของคุณตรวจสอบด้วย REA เอเจนต์สามารถวิเคราะห์แอปโดยไม่ต้องมีซอร์สโค้ด อธิบายการทำงานของฟีเจอร์ แสดงหลักฐาน และสร้างเวอร์ชันสำหรับโปรเจกต์ของคุณได้
|
||||
|
||||
REA เชื่อมเอเจนต์ของคุณกับเครื่องมือสำหรับตรวจสอบไบนารีเนทีฟ แอป JavaScript และ Electron แอสเซมบลี .NET และเว็บไซต์ คุณใช้เครื่องมือเดียวกันจากเทอร์มินัลได้ การวิเคราะห์ทำงานบนเครื่องของคุณ และผลลัพธ์มีทั้งหลักฐานและข้อจำกัดที่ประกอบแต่ละข้อสรุป
|
||||
|
||||
ขั้นตอนตั้งค่าจะลงทะเบียน REA กับเอเจนต์และติดตั้งคำแนะนำการทำงานที่ตรงกัน การวิเคราะห์เนทีฟใช้ Hopper หรือ Ghidra ที่ติดตั้งอยู่แล้วได้ และขั้นตอนตั้งค่าสามารถติดตั้ง Hopper เพิ่มเติมเมื่อได้รับอนุมัติ การวิเคราะห์ JavaScript แบบสถิตไม่ต้องใช้เอนจินทั้งสองนี้
|
||||
|
||||
> **[เยี่ยมชมเว็บไซต์ REA](https://rea.tools/)** เพื่อดูคำแนะนำการตั้งค่า คู่มือพร้อมภาพประกอบ และกรณีศึกษาจริง
|
||||
|
||||
## เริ่มต้นอย่างรวดเร็ว
|
||||
|
||||
### ตั้งค่าเอเจนต์
|
||||
|
||||
เมื่อติดตั้ง Node.js และ npm แล้ว ให้เรียกใช้:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
เลือกเอเจนต์ ตรวจสอบการเปลี่ยนแปลงที่เสนอ แล้วอนุมัติ ขั้นตอนตั้งค่าจะเพิ่มเซิร์ฟเวอร์ MCP ของ REA และคำแนะนำการทำงานที่ตรงกัน พร้อมสำรองการตั้งค่าที่มีอยู่ จากนั้นเริ่มเอเจนต์ใหม่
|
||||
|
||||
ขั้นตอนตั้งค่ารองรับ Claude Code, Codex, Cursor, Gemini CLI, Grok Build และ[เอเจนต์อื่น ๆ](docs/installation.md#supported-agents) ดู[การติดตั้งและตั้งค่า](docs/installation.md) สำหรับการตั้งค่าผู้ให้บริการวิเคราะห์และการลงทะเบียน MCP ด้วยตนเอง
|
||||
|
||||
### ถามเอเจนต์
|
||||
|
||||
```text
|
||||
ตรวจสอบว่าการค้นหาในแอป Notes ทำงานอย่างไร แสดงหลักฐานให้ฉันดู และสร้างฟีเจอร์ที่คล้ายกันสำหรับโปรเจกต์ของฉัน
|
||||
```
|
||||
|
||||
แทนที่ Notes ด้วยแอปเป้าหมายและฟีเจอร์ที่คุณต้องการเข้าใจ
|
||||
|
||||
### ใช้เทอร์มินัล
|
||||
|
||||
ตรวจสอบไดเรกทอรีแอป JavaScript/Electron ที่แตกไฟล์แล้ว หรือไฟล์ ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
ผลลัพธ์ประกอบด้วยโมดูล การนำเข้า ขอบเขตของ Electron และหลักฐาน แทนที่พาธด้วยเป้าหมายของคุณ เช่น `"D:/apps/example"` บน Windows
|
||||
|
||||
หากต้องการติดตั้งคำสั่ง `rea` สำหรับใช้งานเป็นประจำ:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
สำหรับการวิเคราะห์เนทีฟ ให้ตั้งค่าผู้ให้บริการวิเคราะห์ก่อน ดู[คู่มือ CLI และ Evidence](docs/cli.md) สำหรับคำสั่งเนทีฟ การเลือกผู้ให้บริการ สแนปช็อต และการใช้งานในสคริปต์
|
||||
|
||||
### อัปเดต REA
|
||||
|
||||
REA เปลี่ยนแปลงอย่างรวดเร็ว และรุ่นใหม่มีการแก้ไขบั๊กอยู่บ่อยครั้ง โปรดอัปเดตการติดตั้งของคุณให้เป็นปัจจุบัน
|
||||
|
||||
สำหรับ CLI ที่ติดตั้งผ่าน npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
หากต้องการปรับปรุงการลงทะเบียนเอเจนต์และสกิลให้เป็นปัจจุบัน ให้เรียกใช้คำสั่งตั้งค่าที่แสดงหลังการอัปเดต
|
||||
|
||||
หากใช้ `npx` ให้อัปเดตการตั้งค่าเอเจนต์ด้วย:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
ตรวจสอบการเปลี่ยนแปลงการตั้งค่าและเริ่มเอเจนต์ใหม่ สำหรับคำสั่ง CLI ที่ใช้ครั้งเดียว ให้ใช้ `npx rea-agents@latest` ตามด้วยคำสั่งที่ต้องการ
|
||||
|
||||
## REA ทำงานอย่างไร
|
||||
|
||||
เอเจนต์เรียก REA ผ่าน MCP เพื่อตรวจสอบเป้าหมายและติดตามโค้ดที่เกี่ยวข้อง REA ส่งข้อค้นพบกลับมาพร้อมหลักฐาน เอเจนต์ใช้ข้อมูลนี้เพื่อถามคำถามต่อ อธิบายพฤติกรรม หรือเขียนและทดสอบการนำไปใช้งาน คำสั่ง CLI ใช้กระบวนการเดียวกัน
|
||||
|
||||

|
||||
|
||||
[เปิดภาพขนาดเต็ม](website/public/assets/figures/rea-investigation-flow.svg)
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## สิ่งที่วิเคราะห์ได้
|
||||
|
||||
REA ต้องใช้ Node.js 22.x (>=22.19), 24.x (>=24.11) หรือ 26+ และ npm เครื่องมือเพิ่มเติมและแพลตฟอร์มที่รองรับขึ้นอยู่กับเป้าหมาย:
|
||||
|
||||
| เป้าหมาย | สิ่งที่ REA ส่งกลับ | ข้อกำหนดและคู่มือ |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| ไบนารีเนทีฟ | โค้ดเทียม แอสเซมบลี สตริง สัญลักษณ์ การเรียก และการอ้างอิง | Hopper, Ghidra หรือ IDA; [การวิเคราะห์เนทีฟ](https://rea.tools/guides/native/) |
|
||||
| โครงสร้าง ELF แบบออฟไลน์ | เซกชัน เซกเมนต์ สัญลักษณ์/รีโลเคชันต้นฉบับ และตัวเลือกกลไกป้องกันที่พบจากการวิเคราะห์แบบสถิต | pwntools ที่ผู้ใช้จัดเตรียมบน Linux x64; [การวินิจฉัยไบนารี](docs/binary-diagnostics.md) |
|
||||
| ไบต์โค้ด EVM | ตัวเลือกฟังก์ชันสำหรับการเรียก ออฟเซ็ตไบต์ อาร์กิวเมนต์และความสามารถในการเปลี่ยนสถานะที่อนุมานได้ | ไฟล์บนเครื่องที่มีไบต์ดิบหรือข้อมูลเลขฐานสิบหก; [คู่มือ EVM แบบออฟไลน์](docs/evm-bytecode.md) |
|
||||
| ข้อมูลการแครชของ Linux ที่บันทึกไว้ | ระเบียน note ดิบ รีจิสเตอร์/สัญญาณของทุกเธรดที่บันทึกไว้ และตัวเลือกการแมปเพิ่มเติม | pwntools ที่ผู้ใช้จัดเตรียม; เลือกใช้ GDB/pwndbg เพิ่มเติมได้; [การแครชที่บันทึกไว้](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | โมดูล การนำเข้า ซอร์สแมป เส้นทาง IPC และความสัมพันธ์กับส่วนเสริมเนทีฟ | Node.js และ npm; [การวิเคราะห์แอปพลิเคชัน](https://rea.tools/guides/javascript/) |
|
||||
| เว็บไซต์ | โครงสร้างหน้า สคริปต์ ข้อมูลเครือข่ายที่สังเกตได้ และภาพหน้าจอที่ร้องขอ | เบราว์เซอร์ในตระกูล Chrome; [การวิเคราะห์ผ่านเบราว์เซอร์](https://rea.tools/guides/browser/) |
|
||||
| ข้อมูลเครือข่ายที่บันทึกไว้ | คำขอ คำตอบ เพย์โหลดที่ปรากฏ และตำแหน่งต้นทาง | HAR; mitmdump บน Linux สำหรับข้อมูลที่บันทึกในรูปแบบเนทีฟของ mitmproxy; [คู่มือการบันทึกข้อมูล](docs/web-network-captures.md) |
|
||||
| แอสเซมบลี .NET | เมทาดาทา คำสั่ง CIL การพึ่งพาเนทีฟที่ประกาศไว้ และการเปรียบเทียบผลการบิลด์ | การตรวจสอบแบบสถิต; [คู่มือโค้ดแบบจัดการ](docs/managed-code-analysis.md) |
|
||||
| APK ของ Android | การประกาศใน manifest คลาส เมธอดที่ดีคอมไพล์ และการอ้างอิง | JADX แบบไม่มี GUI และ JDK แบบเต็มบน Linux/macOS; [คู่มือ Android](docs/android-analysis.md) |
|
||||
| เฟิร์มแวร์ | พื้นที่ข้อมูล ผลการแตกไฟล์ และการส่งต่อไปวิเคราะห์เนทีฟ | Binwalk / Unblob บน Linux; [คู่มือเฟิร์มแวร์](docs/firmware-analysis.md) |
|
||||
| แพ็กเกจและทรัพยากร | รายการไฟล์ ค่าแฮช plist โครงสร้างบันเดิลของ Apple และทรัพยากรที่แตกออกมา | [คู่มืออาร์ติแฟกต์และ JavaScript](docs/javascript-artifact-reconstruction.md), [แอปพลิเคชัน Apple](docs/apple-application-analysis.md) |
|
||||
| พฤติกรรมของโปรเซส | เอาต์พุตเทอร์มินัล การโต้ตอบ ข้อมูลการสิ้นสุดและระบบไฟล์ที่สังเกตได้ และการเปรียบเทียบการรัน | Linux/macOS ที่มี PTY เนทีฟ; [การบันทึกโปรเซส](docs/process-capture.md) |
|
||||
|
||||
การตรวจสอบ JavaScript และ .NET แบบสถิตอ่านไฟล์ที่ให้มาโดยไม่รันแอปพลิเคชัน การบันทึกขณะทำงานจะรันหรือโต้ตอบกับเป้าหมายที่เลือกโดยใช้สิทธิ์ของผู้ใช้คุณ คู่มือการทำงานแต่ละฉบับอธิบายผลที่เกิดขึ้นไว้
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
รูปแบบไบนารีเนทีฟและแพลตฟอร์มที่รองรับแตกต่างกันตามผู้ให้บริการวิเคราะห์ ดู[การตั้งค่า Hopper และ Ghidra](docs/installation.md#hopper), [คู่มือ IDA](docs/ida-provider.md) และ[การรองรับ Ghidra บน Windows แบบทดลอง](docs/windows-ghidra-p0.md) Ghidra ยังรองรับ[การวิเคราะห์ DOS แบบ 16 บิต](docs/ghidra-dos.md) สำหรับการเลือกผู้ให้บริการ ดู[คู่มือ CLI](docs/cli.md#choose-a-provider) ตรวจสอบ[ความพร้อมใช้งานในแต่ละรุ่น](docs/installation.md#released-package-and-main) สำหรับฟีเจอร์ที่เพิ่มหลังรุ่นล่าสุดบน npm
|
||||
|
||||
## ตัวอย่างการใช้งาน
|
||||
|
||||
### DX-Ball: สร้างการคำนวณแพนเสียงขึ้นใหม่
|
||||
|
||||
ติดตามการเรียกเสียงไปยังฟังก์ชันช่วยแปลงตำแหน่งเป็นค่าแพน ตรวจสอบคำสั่ง และแปลงโค้ดเทียมที่ยังไม่สมบูรณ์เป็น C การสร้างขึ้นใหม่นี้ผ่านการทดสอบ 3,205 กรณีเทียบกับ x86 ต้นฉบับ และสร้างไบต์ทั้ง 63 ไบต์ของฟังก์ชันที่คอมไพล์แล้วได้ตรงกัน
|
||||
|
||||
[อ่านกรณีศึกษา](https://rea.tools/showcase/dx-ball/) · [รีโพซิทอรีการสร้างขึ้นใหม่](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: ติดตามบริดจ์คลิปบอร์ดของ Electron
|
||||
|
||||
ค้นหา API คลิปบอร์ดของ renderer ติดตามผ่าน preload และ IPC ไปยังโปรเซสหลัก แล้วตรวจสอบรูปแบบคลิปบอร์ดที่มีข้อมูลการจัดรูปแบบ
|
||||
|
||||
[อ่านกรณีศึกษา](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: กู้คืนการคำนวณวงแหวนกระสุนของ DOS
|
||||
|
||||
ตรวจสอบคำสั่ง 16 บิตของเกมต้นฉบับบน PC-98 กู้คืนการคำนวณมุมคงที่และมุมเล็งเป้าหมาย แล้วเปรียบเทียบ C++ ที่สร้างขึ้นใหม่กับผลลัพธ์จากคอมไพเลอร์ในยุคนั้น
|
||||
|
||||
[อ่านกรณีศึกษา](https://rea.tools/showcase/th04/) · [รีโพซิทอรีการสร้างขึ้นใหม่](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
หากคุณใช้ REA กับสิ่งที่น่าสนใจ เราอยากเห็นผลงานของคุณ แบ่งปันกรณีของคุณผ่าน [issue](https://github.com/morluto/rea/issues) หรือ [pull request](https://github.com/morluto/rea/pulls) พร้อมระบุเป้าหมาย คำถามของคุณ REA ช่วยอย่างไร และสิ่งที่คุณค้นพบ
|
||||
|
||||
## คำถามที่พบบ่อย
|
||||
|
||||
<details>
|
||||
<summary><strong>เอเจนต์ใดใช้ REA ได้บ้าง?</strong></summary>
|
||||
|
||||
เอเจนต์ใดก็ตามที่รองรับเซิร์ฟเวอร์ MCP บนเครื่อง ขั้นตอนตั้งค่าจะกำหนดค่าให้กับ[เอเจนต์ที่รองรับ](docs/installation.md#supported-agents) ส่วนไคลเอนต์อื่นใช้[การลงทะเบียน MCP ด้วยตนเอง](docs/installation.md#mcp-registry) ได้
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>จำเป็นต้องมี Hopper, Ghidra หรือ IDA หรือไม่?</strong></summary>
|
||||
|
||||
การวิเคราะห์เนทีฟเชิงลึกต้องใช้หนึ่งในเครื่องมือเหล่านี้ การตรวจสอบ JavaScript และ .NET แบบสถิตทำงานได้โดยไม่มีเอนจินวิเคราะห์เนทีฟ ขั้นตอนตั้งค่าสามารถติดตั้ง Hopper หลังได้รับอนุมัติ ส่วน Ghidra และ IDA ใช้การติดตั้งที่คุณมีอยู่แล้ว ดู[การตั้งค่าผู้ให้บริการวิเคราะห์](docs/installation.md#hopper)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>ต้องเริ่ม Hopper ก่อนหรือไม่?</strong></summary>
|
||||
|
||||
REA เริ่ม Hopper เมื่อการดำเนินการต้องใช้ บน macOS อาจมีหน้าต่างเมื่อรันครั้งแรกให้เลือกโหมดทดลองหรือเปิดใช้ใบอนุญาต ดู[การเริ่ม Hopper และการแก้ไขปัญหา](docs/installation.md#launcher-paths-and-troubleshooting)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>การติดตั้งสกิลจาก skills.sh ทำอะไร?</strong></summary>
|
||||
|
||||
สกิลให้คำแนะนำการตรวจสอบแก่เอเจนต์ ใช้ `rea setup` เพื่อลงทะเบียนเซิร์ฟเวอร์ MCP ของ REA และติดตั้งคำแนะนำที่ตรงกัน แล้วเริ่มเอเจนต์ใหม่ ดู[การติดตั้งเฉพาะสกิล](docs/installation.md#skill-only-installation)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA ส่งกลับโค้ดแบบใด?</strong></summary>
|
||||
|
||||
การวิเคราะห์เนทีฟส่งกลับโค้ดเทียมและแอสเซมบลี การวิเคราะห์ JavaScript/Electron กู้คืนโมดูลและความสัมพันธ์ระหว่างโมดูล เอเจนต์ใช้ข้อค้นพบเหล่านี้เพื่อเขียนและทดสอบการนำไปใช้งาน ดูตัวอย่างที่ทำจริงใน[ตัวอย่างการใช้งาน](#ตัวอย่างการใช้งาน)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA อัปโหลดแอปของฉันหรือไม่?</strong></summary>
|
||||
|
||||
REA วิเคราะห์เป้าหมายบนเครื่องของคุณ เอเจนต์ได้รับผลลัพธ์ของเครื่องมือ และผู้ให้บริการโมเดลของเอเจนต์มีนโยบายข้อมูลของตนเอง
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>ควรทำอย่างไรเมื่อพบข้อผิดพลาด?</strong></summary>
|
||||
|
||||
อัปเดตก่อน รุ่นล่าสุดอาจแก้ไขปัญหานี้แล้ว
|
||||
|
||||
สำหรับ CLI ที่ติดตั้งผ่าน npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
สำหรับการตั้งค่าเอเจนต์ผ่าน `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
หากใช้เอเจนต์ ให้ทำ[การปรับปรุงการตั้งค่า](#อัปเดต-rea) ให้เสร็จแล้วเริ่มเอเจนต์ใหม่ ลองงานเดิมอีกครั้ง หากยังพบปัญหา ให้[เปิด issue](https://github.com/morluto/rea/issues) พร้อมระบุรุ่น REA ประเภทเป้าหมาย ขั้นตอนทำให้เกิดปัญหา และข้อความข้อผิดพลาด
|
||||
|
||||
</details>
|
||||
|
||||
## เอกสาร
|
||||
|
||||
เริ่มจาก[คู่มือพร้อมตัวอย่าง](https://rea.tools/guides/) บนเว็บไซต์ สำหรับตัวเลือก ข้อกำหนดเบื้องต้น และสัญญาผลลัพธ์ที่แน่นอน:
|
||||
|
||||
- [การติดตั้งและตั้งค่า](docs/installation.md): การลงทะเบียนเอเจนต์ การตั้งค่าผู้ให้บริการ การอัปเดต และการถอนการติดตั้ง
|
||||
- [ความพร้อมใช้งานและการแก้ไขปัญหา](docs/installation.md#check-readiness-for-your-task): ตรวจวินิจฉัยเอเจนต์หรือเอนจินวิเคราะห์หนึ่งตัว
|
||||
- [CLI และ Evidence](docs/cli.md): คำสั่ง การเลือกผู้ให้บริการ สแนปช็อต การนำเข้า/ส่งออก และรหัสการสิ้นสุด
|
||||
- [สัญญา MCP](docs/mcp-contracts.md) และ[พรอมป์ต์สำหรับเอเจนต์](docs/mcp-prompts.md): ผลลัพธ์เครื่องมือ เซสชัน และการตรวจสอบตามคำแนะนำ
|
||||
- [แค็ตตาล็อกเครื่องมือ](docs/mcp-contracts.md#generated-catalog): รายการเครื่องมือ ผู้ให้บริการ และคำสั่ง CLI ที่สร้างระหว่างการบิลด์
|
||||
- [แผนพัฒนา](docs/roadmap.md): งานที่วางแผนไว้และรายการติดตามความสามารถ
|
||||
|
||||
รายงานช่องโหว่ผ่าน [SECURITY.md](SECURITY.md)
|
||||
|
||||
## การมีส่วนร่วม
|
||||
|
||||
เรายินดีรับความช่วยเหลือในการพัฒนา REA! [เปิด issue](https://github.com/morluto/rea/issues) เพื่อรายงานบั๊กหรือเสนอฟีเจอร์ หรือ[ส่ง pull request](https://github.com/morluto/rea/pulls) เพื่อปรับปรุงโค้ดหรือเอกสาร
|
||||
|
||||
ดู [CONTRIBUTING.md](CONTRIBUTING.md) สำหรับการตั้งค่าสภาพแวดล้อมพัฒนาและการตรวจสอบ ดู[การทดสอบ](docs/testing.md) สำหรับแนวทางการตรวจยืนยัน และ[แผนผังสถาปัตยกรรม](docs/architecture.mermaid) สำหรับโครงสร้างโปรเจกต์
|
||||
|
||||
## ลิงก์ของโปรเจกต์
|
||||
|
||||
[เว็บไซต์](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [ความปลอดภัย](SECURITY.md)
|
||||
|
||||
## ประวัติดาว
|
||||
|
||||
🎉 **30,000 ดาวบน GitHub — ขอบคุณทุกคน!**
|
||||
|
||||
ขอบคุณทุกคนที่ใช้ REA รายงานบั๊ก เสนอฟีเจอร์ ทดสอบบิลด์ และช่วยแก้ไข
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="ประวัติดาว GitHub ของ REA" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## ข้อสงวนสิทธิ์
|
||||
|
||||
REA มีเครื่องมือสำหรับการวิจัยวิศวกรรมย้อนกลับ การวิเคราะห์ และการสร้างขึ้นใหม่อย่างถูกกฎหมาย คุณมีหน้าที่ขออนุญาตตามที่จำเป็นและปฏิบัติตามกฎหมายที่เกี่ยวข้อง โปรเจกต์นี้ไม่สนับสนุนการใช้งานที่ผิดกฎหมายหรือไม่ได้รับอนุญาต
|
||||
|
||||
## สัญญาอนุญาต
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **Türkçe** · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Her şeyi tersine mühendislikle inceleyin
|
||||
|
||||
### İkili dosyaların, uygulamaların ve çalışma zamanı davranışının tersine mühendisliği için tek bir MCP.
|
||||
|
||||
**Beğendiğiniz bir özellik mi gördünüz? İkili kod düzeyine kadar nasıl çalıştığını anlayın.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Web sitesi](https://rea.tools/) · [Rehberler](https://rea.tools/guides/) · [Örnek çalışmalar](https://rea.tools/showcase/)**
|
||||
|
||||
[Hızlı başlangıç](#hızlı-başlangıç) · [REA nasıl çalışır](#rea-nasıl-çalışır) · [Neleri analiz edebilirsiniz](#neleri-analiz-edebilirsiniz) · [Örnek çalışmalar](#örnek-çalışmalar) · [Sık sorulan sorular](#sık-sorulan-sorular) · [Belgeler](#belgeler)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA, yerel bir ikili dosyayı incelerken Hopper içinde analiz köprüsünü başlatıyor" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA topluluğu">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Tersine mühendislik topluluğuna katılın</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Soru-cevap · Sonuç paylaşımı</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Bir uygulamada kendi ürününüze eklemek istediğiniz bir özellik mi gördünüz? Ajanınızdan REA ile araştırmasını isteyin. Kaynak kodu olmadan uygulamayı inceleyebilir, özelliğin nasıl çalıştığını açıklayabilir, kanıtları gösterebilir ve projenize uygun bir sürümünü geliştirebilir.
|
||||
|
||||
REA, ajanınızı yerel makine kodu içeren ikili dosyaları, JavaScript ve Electron uygulamalarını, .NET derlemelerini ve web sitelerini inceleyen araçlara bağlar. Aynı araçları terminalden de kullanabilirsiniz. Analiz yerel olarak çalışır; sonuçlar her çıkarımın dayandığı kanıtları ve sınırlamaları içerir.
|
||||
|
||||
Kurulum, REA'yı ajanınıza kaydeder ve uyumlu iş akışı talimatlarını yükler. Yerel makine kodu analizi mevcut bir Hopper veya Ghidra kurulumunu kullanabilir; kurulum işlemi, onayınızla isteğe bağlı olarak Hopper da yükleyebilir. Statik JavaScript analizi bu iki motora da ihtiyaç duymaz.
|
||||
|
||||
> Kurulum talimatları, görselli rehberler ve gerçek örnek çalışmalar için **[REA web sitesini ziyaret edin](https://rea.tools/)**.
|
||||
|
||||
## Hızlı başlangıç
|
||||
|
||||
### Ajanınızı yapılandırın
|
||||
|
||||
Node.js ve npm yüklüyken şu komutu çalıştırın:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Ajanlarınızı seçin, önerilen değişiklikleri inceleyin ve onaylayın. Kurulum, mevcut yapılandırmaları yedekleyerek REA'nın MCP sunucusunu ve uyumlu iş akışı talimatlarını ekler. Ardından ajanınızı yeniden başlatın.
|
||||
|
||||
Kurulum Claude Code, Codex, Cursor, Gemini CLI, Grok Build ve [diğer ajanları](docs/installation.md#supported-agents) destekler. Sağlayıcı yapılandırması ve elle MCP kaydı için [yükleme ve kurulum](docs/installation.md) belgesine bakın.
|
||||
|
||||
### Ajanınıza sorun
|
||||
|
||||
```text
|
||||
Notes uygulamasında aramanın nasıl çalıştığını araştır, kanıtları göster ve projem için benzer bir özellik geliştir.
|
||||
```
|
||||
|
||||
Notes yerine hedef uygulamanızı ve anlamak istediğiniz özelliği belirtin.
|
||||
|
||||
### Terminali kullanın
|
||||
|
||||
Çıkarılmış bir JavaScript/Electron uygulama dizinini veya ASAR dosyasını inceleyin:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Sonuç; modülleri, içe aktarmaları, Electron sınırlarını ve bunlara ilişkin kanıtları içerir. Yolu hedefinizle değiştirin; Windows'ta örneğin `"D:/apps/example"` kullanabilirsiniz.
|
||||
|
||||
Düzenli kullanım için `rea` komutunu yüklemek üzere:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Yerel makine kodu analizi için önce bir sağlayıcı yapılandırın. Yerel analiz komutları, sağlayıcı seçimi, anlık görüntüler ve betik kullanımı için [CLI ve Evidence rehberine](docs/cli.md) bakın.
|
||||
|
||||
### REA'yı güncelleyin
|
||||
|
||||
REA hızla değişir ve yeni sürümler sık sık hata düzeltmeleri içerir. Kurulumunuzu güncel tutun.
|
||||
|
||||
npm ile yüklenen CLI için:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Ajan kayıtlarını ve skill'i güncellemek için güncellemenin çıktısında gösterilen kurulum komutunu çalıştırın.
|
||||
|
||||
`npx` kullanıyorsanız ajan kurulumunu şu komutla güncelleyin:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Kurulum değişikliklerini inceleyin ve ajanınızı yeniden başlatın. Tek seferlik CLI işlemleri için `npx rea-agents@latest` komutunun ardından istediğiniz komutu yazın.
|
||||
|
||||
## REA nasıl çalışır
|
||||
|
||||
Ajanınız, hedefi incelemek ve ilgili kodu izlemek için MCP üzerinden REA'yı çağırır. REA bulguları kanıtlarıyla birlikte döndürür. Ajan bunları takip soruları sormak, davranışı açıklamak veya bir uygulama yazıp test etmek için kullanır. CLI komutları aynı iş akışlarını kullanır.
|
||||
|
||||

|
||||
|
||||
[Şekli tam boyutuyla açın](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Neleri analiz edebilirsiniz
|
||||
|
||||
REA; Node.js 22.x (>=22.19), 24.x (>=24.11) veya 26+ ve npm gerektirir. Ek araçlar ve desteklenen ana bilgisayar sistemleri hedefe bağlıdır:
|
||||
|
||||
| Hedef | REA'nın döndürdükleri | Gereksinimler ve rehber |
|
||||
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Yerel makine kodu içeren ikili dosyalar | Sözde kod, assembly kodu, dizeler, semboller, çağrılar ve referanslar | Hopper, Ghidra veya IDA; [yerel kod analizi](https://rea.tools/guides/native/) |
|
||||
| Çevrimdışı ELF yapısı | Bölümler, segmentler, özgün semboller/yeniden konumlandırmalar ve statik analizden olası güvenlik önlemleri | Linux x64 üzerinde çağıranın sağladığı pwntools; [ikili dosya tanılama](docs/binary-diagnostics.md) |
|
||||
| EVM bayt kodu | Yönlendirme seçicileri, bayt ofsetleri, çıkarımla belirlenen argümanlar ve durum değiştirilebilirliği | Yerel ham bayt/onaltılık girdi; [çevrimdışı EVM rehberi](docs/evm-bytecode.md) |
|
||||
| Kaydedilmiş Linux çökmeleri | Ham note kayıtları, kaydedilen her iş parçacığının yazmaçları/sinyalleri ve isteğe bağlı eşleme adayları | Çağıranın sağladığı pwntools; isteğe bağlı GDB/pwndbg; [kaydedilmiş çökmeler](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modüller, içe aktarmalar, kaynak haritaları, rotalar, IPC ve yerel eklenti ilişkileri | Node.js ve npm; [uygulama analizi](https://rea.tools/guides/javascript/) |
|
||||
| Web siteleri | Sayfa yapısı, betikler, ağ gözlemleri ve istenen ekran görüntüleri | Chrome ailesinden bir tarayıcı; [tarayıcı analizi](https://rea.tools/guides/browser/) |
|
||||
| Kaydedilmiş ağ yakalamaları | İstekler, yanıtlar, erişilebilir yük içerikleri ve kaynak konumları | HAR; mitmproxy'nin kendi biçimindeki yakalamalar için Linux üzerinde mitmdump; [ağ yakalama rehberi](docs/web-network-captures.md) |
|
||||
| .NET derlemeleri | Meta veriler, CIL talimatları, bildirilmiş yerel bağımlılıklar ve derleme karşılaştırmaları | Statik inceleme; [yönetilen kod rehberi](docs/managed-code-analysis.md) |
|
||||
| Android APK'leri | Manifest bildirimleri, sınıflar, tersine derlenmiş metotlar ve referanslar | Linux/macOS üzerinde arayüzsüz JADX ve tam bir JDK; [Android rehberi](docs/android-analysis.md) |
|
||||
| Donanım yazılımı | Bölgeler, çıkarma sonuçları ve yerel kod analizine devirler | Linux üzerinde Binwalk / Unblob; [donanım yazılımı rehberi](docs/firmware-analysis.md) |
|
||||
| Paketler ve kaynaklar | Dosya envanterleri, özet değerleri, plist'ler, Apple paket yapısı ve çıkarılmış kaynaklar | [Artefakt ve JavaScript rehberi](docs/javascript-artifact-reconstruction.md), [Apple uygulamaları](docs/apple-application-analysis.md) |
|
||||
| Süreç davranışı | Terminal çıktısı, etkileşimler, sonlanma ve dosya sistemi gözlemleri, çalıştırma karşılaştırmaları | Yerel PTY ile Linux/macOS; [süreç yakalama](docs/process-capture.md) |
|
||||
|
||||
Statik JavaScript ve .NET incelemesi, uygulamayı çalıştırmadan sağlanan dosyaları okur. Çalışma zamanı yakalaması, seçilen hedefi kullanıcı izinlerinizle çalıştırır veya onunla etkileşime girer; her çalışma zamanı rehberi etkilerini açıklar.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Yerel makine kodu içeren ikili dosya biçimleri ve ana bilgisayar desteği sağlayıcıya göre değişir. [Hopper ve Ghidra kurulumu](docs/installation.md#hopper), [IDA rehberi](docs/ida-provider.md) ve [Windows için deneysel Ghidra desteği](docs/windows-ghidra-p0.md) belgelerine bakın. Ghidra ayrıca [16 bit DOS analizini](docs/ghidra-dos.md) destekler. Sağlayıcı seçimi için [CLI rehberine](docs/cli.md#choose-a-provider) bakın. En son npm sürümünden sonra eklenen özellikler için [sürümlerde kullanılabilirliği](docs/installation.md#released-package-and-main) kontrol edin.
|
||||
|
||||
## Örnek çalışmalar
|
||||
|
||||
### DX-Ball: Sesin stereo konumlandırma hesabını yeniden oluşturma
|
||||
|
||||
Bir ses çağrısından konumu stereo konumlandırmaya dönüştüren yardımcı fonksiyona ilerleyin, talimatları inceleyin ve eksik sözde kodu C'ye dönüştürün. Yeniden oluşturulan kod, özgün x86 üzerinde 3.205 test durumunu geçer ve derlenmiş fonksiyonun 63 baytının tamamını yeniden üretir.
|
||||
|
||||
[Örnek çalışmayı okuyun](https://rea.tools/showcase/dx-ball/) · [Yeniden oluşturma deposu](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: Electron pano köprüsünü izleme
|
||||
|
||||
İşleyici sürecin pano API'sini bulun, preload ve IPC üzerinden ana sürece kadar izleyin ve zengin pano biçimini inceleyin.
|
||||
|
||||
[Örnek çalışmayı okuyun](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: DOS mermi halkası hesabını geri çıkarma
|
||||
|
||||
Özgün PC-98 oyununun 16 bit talimatlarını inceleyin, sabit ve hedefe yönelik açı hesaplarını geri çıkarın ve yeniden oluşturulan C++ kodunu dönemin derleyici çıktısıyla karşılaştırın.
|
||||
|
||||
[Örnek çalışmayı okuyun](https://rea.tools/showcase/th04/) · [Yeniden oluşturma deposu](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
REA'yı ilginç bir konuda kullandıysanız görmek isteriz. Hedefi, sorunuzu, REA'nın nasıl yardımcı olduğunu ve bulgularınızı bir [issue](https://github.com/morluto/rea/issues) veya [pull request](https://github.com/morluto/rea/pulls) ile paylaşın.
|
||||
|
||||
## Sık sorulan sorular
|
||||
|
||||
<details>
|
||||
<summary><strong>Hangi ajanlar REA'yı kullanabilir?</strong></summary>
|
||||
|
||||
Yerel MCP sunucularını destekleyen her ajan REA'yı kullanabilir. Kurulum, [desteklenen ajanları](docs/installation.md#supported-agents) yapılandırır; diğer istemciler [elle MCP kaydını](docs/installation.md#mcp-registry) kullanabilir.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Hopper, Ghidra veya IDA gerekiyor mu?</strong></summary>
|
||||
|
||||
Derin yerel kod analizi bunlardan birini kullanır. Statik JavaScript ve .NET incelemesi yerel analiz motoru olmadan çalışır. Kurulum onaydan sonra Hopper yükleyebilir; Ghidra ve IDA mevcut kurulumlarınızı kullanır. [Sağlayıcı kurulumuna](docs/installation.md#hopper) bakın.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Önce Hopper'ı başlatmam gerekiyor mu?</strong></summary>
|
||||
|
||||
REA, bir işlem gerektirdiğinde Hopper'ı başlatır. macOS'ta ilk çalıştırmada demo modunu seçmenizi veya lisansınızı etkinleştirmenizi isteyen bir iletişim kutusu açılabilir. [Hopper başlatma ve sorun giderme](docs/installation.md#launcher-paths-and-troubleshooting) belgesine bakın.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>skills.sh üzerinden skill yüklemek ne yapar?</strong></summary>
|
||||
|
||||
Skill, ajanınıza araştırma talimatları sağlar. REA'nın MCP sunucusunu kaydetmek ve uyumlu talimatları yüklemek için `rea setup` kullanın, ardından ajanınızı yeniden başlatın. [Yalnızca skill yükleme](docs/installation.md#skill-only-installation) belgesine bakın.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA hangi kodu döndürür?</strong></summary>
|
||||
|
||||
Yerel kod analizi sözde kod ve assembly kodu döndürür. JavaScript/Electron analizi modülleri ve aralarındaki ilişkileri geri çıkarır. Ajanınız bu bulgularla bir uygulama yazar ve test eder; [örnek çalışmalar](#örnek-çalışmalar) uygulamalı örnekler sunar.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA uygulamamı yükler mi?</strong></summary>
|
||||
|
||||
REA hedefleri yerel olarak analiz eder. Ajanınız araç sonuçlarını alır ve model sağlayıcısının kendi veri politikası vardır.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Bir hatayla karşılaşırsam ne yapmalıyım?</strong></summary>
|
||||
|
||||
Önce güncelleyin; yakın tarihli bir sürüm hatayı düzeltmiş olabilir.
|
||||
|
||||
npm ile yüklenen CLI için:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`npx` üzerinden ajan kurulumu için:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Ajan kullanıyorsanız [kurulum yenilemesini](#reayı-güncelleyin) tamamlayın ve ajanı yeniden başlatın. Aynı görevi tekrar deneyin. Sorun sürerse REA sürümünüzü, hedef türünü, yeniden üretme adımlarını ve hata çıktısını içeren bir [issue açın](https://github.com/morluto/rea/issues).
|
||||
|
||||
</details>
|
||||
|
||||
## Belgeler
|
||||
|
||||
Web sitesindeki [uygulamalı rehberlerden](https://rea.tools/guides/) başlayın. Kesin seçenekler, ön koşullar ve sonuç sözleşmeleri için:
|
||||
|
||||
- [Yükleme ve kurulum](docs/installation.md): ajan kaydı, sağlayıcı yapılandırması, güncellemeler ve kaldırma.
|
||||
- [Hazırlık ve sorun giderme](docs/installation.md#check-readiness-for-your-task): belirli bir ajanı veya analiz motorunu tanılama.
|
||||
- [CLI ve Evidence](docs/cli.md): komutlar, sağlayıcı seçimi, anlık görüntüler, içe/dışa aktarma ve çıkış durumları.
|
||||
- [MCP sözleşmeleri](docs/mcp-contracts.md) ve [ajan istemleri](docs/mcp-prompts.md): araç sonuçları, oturumlar ve yönlendirmeli araştırmalar.
|
||||
- [Araç kataloğu](docs/mcp-contracts.md#generated-catalog): derleme sırasında oluşturulan araç, sağlayıcı ve CLI komutu envanteri.
|
||||
- [Yol haritası](docs/roadmap.md): planlanan çalışmalar ve yetenek takibi.
|
||||
|
||||
Güvenlik açıklarını [SECURITY.md](SECURITY.md) üzerinden bildirin.
|
||||
|
||||
## Katkıda bulunma
|
||||
|
||||
REA'ya katkınızı bekliyoruz! Hata bildirmek veya özellik önermek için bir [issue açın](https://github.com/morluto/rea/issues); kodu veya belgeleri geliştirmek için bir [pull request gönderin](https://github.com/morluto/rea/pulls).
|
||||
|
||||
Geliştirme ortamı ve kontroller için [CONTRIBUTING.md](CONTRIBUTING.md), doğrulama süreçleri için [test rehberi](docs/testing.md) ve proje yapısı için [mimari harita](docs/architecture.mermaid) belgelerine bakın.
|
||||
|
||||
## Proje bağlantıları
|
||||
|
||||
[Web sitesi](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Güvenlik](SECURITY.md)
|
||||
|
||||
## Yıldız geçmişi
|
||||
|
||||
🎉 **GitHub'da 30.000 yıldız — teşekkürler!**
|
||||
|
||||
REA'yı kullanan, hata bildiren, özellik talep eden, derlemeleri test eden ve düzeltmelere katkıda bulunan herkese teşekkürler.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub yıldız geçmişi" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Sorumluluk reddi
|
||||
|
||||
REA, yasal tersine mühendislik araştırması, analizi ve yeniden oluşturma için araçlar sağlar. Gerekli izinleri almak ve geçerli yasalara uymak sizin sorumluluğunuzdadır. Proje yasa dışı veya yetkisiz kullanımı desteklemez.
|
||||
|
||||
## Lisans
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · **Українська** · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Реверс-інжиніринг будь-чого
|
||||
|
||||
### Один MCP для реверс-інжинірингу бінарних файлів, застосунків і поведінки під час виконання.
|
||||
|
||||
**Побачили цікаву функцію? Зрозумійте, як вона працює, аж до рівня двійкового коду.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Сайт](https://rea.tools/) · [Посібники](https://rea.tools/guides/) · [Приклади](https://rea.tools/showcase/)**
|
||||
|
||||
[Швидкий старт](#швидкий-старт) · [Як працює REA](#як-працює-rea) · [Що можна аналізувати](#що-можна-аналізувати) · [Приклади](#приклади) · [Поширені запитання](#поширені-запитання) · [Документація](#документація)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA запускає міст аналізу всередині Hopper під час дослідження нативного бінарного файла" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Спільнота REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Приєднуйтеся до спільноти реверс-інжинірингу</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Запитання й відповіді · Обмін результатами</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Побачили в застосунку функцію, яку хочете додати до свого продукту? Попросіть агента дослідити її за допомогою REA. Він може перевірити застосунок без вихідного коду, пояснити роботу функції, показати підтвердження та створити реалізацію для вашого проєкту.
|
||||
|
||||
REA підключає вашого агента до інструментів дослідження нативних бінарних файлів, застосунків JavaScript і Electron, збірок .NET та сайтів. Ті самі інструменти можна використовувати з термінала. Аналіз виконується локально, а результати містять підтвердження й обмеження, на яких ґрунтується кожен висновок.
|
||||
|
||||
Налаштування реєструє REA у вашому агенті та встановлює відповідні інструкції робочих процесів. Для нативного аналізу можна використати вже встановлений Hopper або Ghidra; налаштування також може встановити Hopper за вашою згодою. Статичному аналізу JavaScript не потрібен жоден із цих рушіїв.
|
||||
|
||||
> **[Відвідайте сайт REA](https://rea.tools/)**, щоб знайти інструкції налаштування, ілюстровані посібники та реальні дослідження.
|
||||
|
||||
## Швидкий старт
|
||||
|
||||
### Налаштуйте агента
|
||||
|
||||
Якщо Node.js і npm встановлено, виконайте:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Виберіть агентів, перевірте запропоновані зміни та схваліть їх. Налаштування додає MCP-сервер REA та відповідні інструкції робочих процесів, створюючи резервні копії наявної конфігурації. Після цього перезапустіть агента.
|
||||
|
||||
Налаштування підтримує Claude Code, Codex, Cursor, Gemini CLI, Grok Build та [інших агентів](docs/installation.md#supported-agents). Конфігурацію провайдерів і ручну реєстрацію MCP описано в розділі [встановлення й налаштування](docs/installation.md).
|
||||
|
||||
### Запитайте агента
|
||||
|
||||
```text
|
||||
Досліди, як працює пошук у застосунку Notes, покажи підтвердження й створи подібну функцію для мого проєкту.
|
||||
```
|
||||
|
||||
Замініть Notes цільовим застосунком і вкажіть функцію, яку хочете зрозуміти.
|
||||
|
||||
### Використовуйте термінал
|
||||
|
||||
Перевірте розпакований каталог застосунку JavaScript/Electron або файл ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Результат містить модулі, імпорти, межі Electron і підтвердження для них. Замініть шлях своїм, наприклад `"D:/apps/example"` у Windows.
|
||||
|
||||
Для регулярного використання встановіть команду `rea`:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Для нативного аналізу спочатку налаштуйте провайдера. Нативні команди, вибір провайдера, знімки та використання у скриптах описує [посібник CLI та Evidence](docs/cli.md).
|
||||
|
||||
### Оновіть REA
|
||||
|
||||
REA швидко розвивається, а нові випуски часто містять виправлення помилок. Підтримуйте встановлену версію актуальною.
|
||||
|
||||
Для CLI, встановленого через npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Щоб оновити реєстрації агентів і skill, виконайте команду налаштування, яку виведе оновлення.
|
||||
|
||||
Якщо використовуєте `npx`, оновіть налаштування агента командою:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Перевірте зміни налаштувань і перезапустіть агента. Для разових команд CLI використовуйте `npx rea-agents@latest`, а потім потрібну команду.
|
||||
|
||||
## Як працює REA
|
||||
|
||||
Агент викликає REA через MCP, щоб перевірити ціль і простежити пов'язаний код. REA повертає знахідки разом із підтвердженнями. Агент використовує їх для уточнювальних запитань, пояснення поведінки або написання й тестування реалізації. Команди CLI використовують ті самі робочі процеси.
|
||||
|
||||

|
||||
|
||||
[Відкрити зображення в повному розмірі](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Що можна аналізувати
|
||||
|
||||
REA потребує Node.js 22.x (>=22.19), 24.x (>=24.11) або 26+, а також npm. Додаткові інструменти й підтримувані системи залежать від цілі:
|
||||
|
||||
| Ціль | Що повертає REA | Вимоги й посібник |
|
||||
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Нативні бінарні файли | Псевдокод, асемблерний код, рядки, символи, виклики й посилання | Hopper, Ghidra або IDA; [нативний аналіз](https://rea.tools/guides/native/) |
|
||||
| Структура ELF без запуску | Секції, сегменти, початкові символи/релокації та можливі захисні механізми за даними статичного аналізу | pwntools, наданий стороною, що викликає інструмент, у Linux x64; [діагностика бінарних файлів](docs/binary-diagnostics.md) |
|
||||
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення; [посібник EVM без запуску](docs/evm-bytecode.md) |
|
||||
| Записані аварійні завершення в Linux | Необроблені записи note, регістри/сигнали кожного записаного потоку й необов'язкові кандидати зіставлення пам'яті | pwntools, наданий стороною, що викликає інструмент; необов'язкові GDB/pwndbg; [записані аварійні завершення](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Модулі, імпорти, карти вихідного коду, маршрути, IPC і зв'язки з нативними доповненнями | Node.js і npm; [аналіз застосунків](https://rea.tools/guides/javascript/) |
|
||||
| Сайти | Структура сторінки, скрипти, мережеві спостереження й запитані знімки екрана | Браузер сімейства Chrome; [аналіз браузера](https://rea.tools/guides/browser/) |
|
||||
| Збережені мережеві захоплення | Запити, відповіді, доступні корисні дані та місця в джерелі | HAR; mitmdump у Linux для захоплень у власному форматі mitmproxy; [посібник мережевих захоплень](docs/web-network-captures.md) |
|
||||
| Збірки .NET | Метадані, інструкції CIL, оголошені нативні залежності й порівняння збірок | Статична перевірка; [посібник керованого коду](docs/managed-code-analysis.md) |
|
||||
| Android APK | Оголошення маніфесту, класи, декомпільовані методи й посилання | JADX без графічного інтерфейсу й повний JDK у Linux/macOS; [посібник Android](docs/android-analysis.md) |
|
||||
| Прошивки | Області, результати видобування й передавання до нативного аналізу | Binwalk / Unblob у Linux; [посібник прошивок](docs/firmware-analysis.md) |
|
||||
| Пакети й ресурси | Переліки файлів, хеші, plist, структура пакетів Apple та видобуті ресурси | [Посібник артефактів і JavaScript](docs/javascript-artifact-reconstruction.md), [застосунки Apple](docs/apple-application-analysis.md) |
|
||||
| Поведінка процесів | Вивід термінала, взаємодії, спостереження за завершенням і файловою системою та порівняння запусків | Linux/macOS із нативним PTY; [захоплення процесів](docs/process-capture.md) |
|
||||
|
||||
Статична перевірка JavaScript і .NET читає надані файли, не запускаючи застосунок. Захоплення під час виконання запускає вибрану ціль або взаємодіє з нею з правами вашого користувача; відповідні посібники описують його вплив.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Нативні формати й підтримка систем відрізняються залежно від провайдера. Дивіться [налаштування Hopper і Ghidra](docs/installation.md#hopper), [посібник IDA](docs/ida-provider.md) та [експериментальну підтримку Ghidra у Windows](docs/windows-ghidra-p0.md). Ghidra також підтримує [аналіз 16-бітного DOS](docs/ghidra-dos.md). Для вибору провайдера дивіться [посібник CLI](docs/cli.md#choose-a-provider). Для функцій, доданих після найновішої версії в npm, перевірте [доступність у випусках](docs/installation.md#released-package-and-main).
|
||||
|
||||
## Приклади
|
||||
|
||||
### DX-Ball: відновлення розрахунку звукової панорами
|
||||
|
||||
Простежте звуковий виклик до допоміжної функції, що перетворює координату на панорамування, перевірте інструкції та перетворіть неповний псевдокод на C. Відновлена реалізація проходить 3 205 тестів з оригінальним x86-кодом і відтворює всі 63 байти скомпільованої функції.
|
||||
|
||||
[Прочитати дослідження](https://rea.tools/showcase/dx-ball/) · [Репозиторій реконструкції](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: відстеження мосту буфера обміну Electron
|
||||
|
||||
Знайдіть API буфера обміну в процесі рендерингу, простежте його через preload та IPC до головного процесу й перевірте формат буфера обміну з даними форматування.
|
||||
|
||||
[Прочитати дослідження](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: відновлення розрахунку кільця куль у DOS
|
||||
|
||||
Перевірте 16-бітні інструкції оригінальної гри для PC-98, відновіть розрахунки фіксованих і прицільних кутів та порівняйте відновлений C++ із виводом тогочасного компілятора.
|
||||
|
||||
[Прочитати дослідження](https://rea.tools/showcase/th04/) · [Репозиторій реконструкції](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Якщо ви дослідили з REA щось цікаве, поділіться результатом. Створіть [issue](https://github.com/morluto/rea/issues) або [pull request](https://github.com/morluto/rea/pulls) і вкажіть ціль, ваше запитання, як допоміг REA та що ви з'ясували.
|
||||
|
||||
## Поширені запитання
|
||||
|
||||
<details>
|
||||
<summary><strong>Які агенти можуть використовувати REA?</strong></summary>
|
||||
|
||||
Будь-який агент, що підтримує локальні MCP-сервери. Налаштування конфігурує [підтримуваних агентів](docs/installation.md#supported-agents); інші клієнти можуть використати [ручну реєстрацію MCP](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Чи потрібні мені Hopper, Ghidra або IDA?</strong></summary>
|
||||
|
||||
Поглиблений нативний аналіз використовує один із цих інструментів. Статична перевірка JavaScript і .NET працює без рушія нативного аналізу. Налаштування може встановити Hopper після схвалення; Ghidra й IDA використовують наявні встановлення. Дивіться [налаштування провайдерів](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Чи потрібно спочатку запускати Hopper?</strong></summary>
|
||||
|
||||
REA запускає Hopper, коли він потрібен для операції. У macOS під час першого запуску може з'явитися діалог вибору деморежиму або активації ліцензії. Дивіться [запуск Hopper та усунення неполадок](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Що дає встановлення skill із skills.sh?</strong></summary>
|
||||
|
||||
Skill надає агенту інструкції дослідження. Використовуйте `rea setup`, щоб зареєструвати MCP-сервер REA та встановити відповідні інструкції, а потім перезапустіть агента. Дивіться [встановлення лише skill](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Який код повертає REA?</strong></summary>
|
||||
|
||||
Нативний аналіз повертає псевдокод і асемблерний код. Аналіз JavaScript/Electron відновлює модулі та їхні зв'язки. Агент використовує ці результати, щоб написати й протестувати реалізацію; [приклади](#приклади) містять практичні дослідження.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Чи завантажує REA мій застосунок на сервер?</strong></summary>
|
||||
|
||||
REA аналізує цілі локально. Ваш агент отримує результати інструментів, а постачальник його моделі має власну політику даних.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Що робити, якщо я зіткнувся з помилкою?</strong></summary>
|
||||
|
||||
Спочатку оновіться: нещодавній випуск уже міг виправити помилку.
|
||||
|
||||
Для CLI, встановленого через npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Для налаштування агента через `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Якщо використовуєте агента, завершіть [оновлення налаштувань](#оновіть-rea) і перезапустіть його. Повторіть те саме завдання. Якщо проблема залишається, [відкрийте issue](https://github.com/morluto/rea/issues) із версією REA, типом цілі, кроками відтворення та виводом помилки.
|
||||
|
||||
</details>
|
||||
|
||||
## Документація
|
||||
|
||||
Почніть із [практичних посібників](https://rea.tools/guides/) на сайті. Точні параметри, передумови й контракти результатів описано тут:
|
||||
|
||||
- [Встановлення й налаштування](docs/installation.md): реєстрація агентів, конфігурація провайдерів, оновлення та видалення.
|
||||
- [Готовність та усунення неполадок](docs/installation.md#check-readiness-for-your-task): діагностика конкретного агента або рушія аналізу.
|
||||
- [CLI та Evidence](docs/cli.md): команди, вибір провайдера, знімки, імпорт/експорт і статуси завершення.
|
||||
- [Контракти MCP](docs/mcp-contracts.md) і [промпти агентів](docs/mcp-prompts.md): результати інструментів, сеанси й керовані дослідження.
|
||||
- [Каталог інструментів](docs/mcp-contracts.md#generated-catalog): створюваний під час збірки перелік інструментів, провайдерів і команд CLI.
|
||||
- [План розвитку](docs/roadmap.md): заплановані роботи й відстеження можливостей.
|
||||
|
||||
Повідомляйте про вразливості згідно з [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Участь у проєкті
|
||||
|
||||
Будемо раді вашій допомозі з REA! [Відкрийте issue](https://github.com/morluto/rea/issues), щоб повідомити про помилку чи запропонувати функцію, або [надішліть pull request](https://github.com/morluto/rea/pulls) з поліпшеннями коду чи документації.
|
||||
|
||||
Налаштування середовища розробки й перевірки описано в [CONTRIBUTING.md](CONTRIBUTING.md), шляхи верифікації — у [посібнику тестування](docs/testing.md), а структуру проєкту — на [схемі архітектури](docs/architecture.mermaid).
|
||||
|
||||
## Посилання проєкту
|
||||
|
||||
[Сайт](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Безпека](SECURITY.md)
|
||||
|
||||
## Історія зірок
|
||||
|
||||
🎉 **30 000 зірок на GitHub — дякуємо!**
|
||||
|
||||
Дякуємо всім, хто використовує REA, повідомляє про помилки, пропонує нові функції, тестує збірки й допомагає з виправленнями.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Історія зірок REA на GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Відмова від відповідальності
|
||||
|
||||
REA надає інструменти для законних досліджень, аналізу й реконструкції за допомогою реверс-інжинірингу. Ви відповідаєте за отримання необхідних дозволів і дотримання застосовних законів. Проєкт не підтримує незаконне або несанкціоноване використання.
|
||||
|
||||
## Ліцензія
|
||||
|
||||
[MIT](LICENSE)
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · **Tiếng Việt** · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA: Kỹ thuật dịch ngược cho mọi thứ
|
||||
|
||||
### Một MCP để dịch ngược tệp nhị phân, ứng dụng và hành vi khi chạy.
|
||||
|
||||
**Thấy một tính năng bạn thích? Hiểu cách nó hoạt động, đến tận mức mã nhị phân.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Trang web](https://rea.tools/) · [Hướng dẫn](https://rea.tools/guides/) · [Các nghiên cứu điển hình](https://rea.tools/showcase/)**
|
||||
|
||||
[Bắt đầu nhanh](#bắt-đầu-nhanh) · [Cách REA hoạt động](#cách-rea-hoạt-động) · [Những gì bạn có thể phân tích](#những-gì-bạn-có-thể-phân-tích) · [Các nghiên cứu điển hình](#các-nghiên-cứu-điển-hình) · [Câu hỏi thường gặp](#câu-hỏi-thường-gặp) · [Tài liệu](#tài-liệu)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA khởi chạy cầu nối phân tích bên trong Hopper khi kiểm tra tệp nhị phân mã máy" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="Cộng đồng REA">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Tham gia cộng đồng kỹ thuật dịch ngược</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Hỏi đáp · Chia sẻ kết quả</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Bạn thấy một tính năng trong ứng dụng và muốn đưa nó vào sản phẩm của mình? Hãy nhờ tác nhân của bạn điều tra bằng REA. Tác nhân có thể kiểm tra ứng dụng mà không cần mã nguồn, giải thích cách tính năng hoạt động, đưa ra bằng chứng và xây dựng một phiên bản cho dự án của bạn.
|
||||
|
||||
REA kết nối tác nhân của bạn với các công cụ kiểm tra tệp nhị phân mã máy, ứng dụng JavaScript và Electron, assembly .NET và trang web. Bạn cũng có thể dùng các công cụ đó từ terminal. Việc phân tích chạy cục bộ; kết quả bao gồm bằng chứng và giới hạn làm cơ sở cho từng kết luận.
|
||||
|
||||
Quá trình thiết lập đăng ký REA với tác nhân và cài đặt các hướng dẫn quy trình tương ứng. Phân tích mã máy có thể sử dụng Hopper hoặc Ghidra đã cài sẵn; quá trình thiết lập cũng có thể cài Hopper nếu bạn chấp thuận. Phân tích tĩnh JavaScript không cần công cụ nào trong hai công cụ này.
|
||||
|
||||
> **[Truy cập trang web REA](https://rea.tools/)** để xem cách thiết lập, hướng dẫn có hình minh họa và các nghiên cứu điển hình thực tế.
|
||||
|
||||
## Bắt đầu nhanh
|
||||
|
||||
### Thiết lập tác nhân
|
||||
|
||||
Sau khi cài Node.js và npm, chạy:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Chọn các tác nhân, xem lại những thay đổi được đề xuất và chấp thuận. Quá trình thiết lập thêm máy chủ MCP của REA cùng hướng dẫn quy trình tương ứng, đồng thời sao lưu cấu hình hiện có. Sau đó khởi động lại tác nhân.
|
||||
|
||||
Quá trình thiết lập hỗ trợ Claude Code, Codex, Cursor, Gemini CLI, Grok Build và [các tác nhân khác](docs/installation.md#supported-agents). Xem [cài đặt và thiết lập](docs/installation.md) để cấu hình trình phân tích và đăng ký MCP thủ công.
|
||||
|
||||
### Hỏi tác nhân
|
||||
|
||||
```text
|
||||
Tìm hiểu cách tính năng tìm kiếm trong ứng dụng Notes hoạt động, cho tôi xem bằng chứng và xây dựng tính năng tương tự cho dự án của tôi.
|
||||
```
|
||||
|
||||
Thay Notes bằng ứng dụng mục tiêu và chỉ rõ tính năng bạn muốn hiểu.
|
||||
|
||||
### Sử dụng terminal
|
||||
|
||||
Kiểm tra thư mục ứng dụng JavaScript/Electron đã giải nén hoặc tệp ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Kết quả bao gồm mô-đun, các khai báo import, ranh giới Electron và bằng chứng tương ứng. Thay đường dẫn bằng mục tiêu của bạn, chẳng hạn `"D:/apps/example"` trên Windows.
|
||||
|
||||
Để cài lệnh `rea` cho việc sử dụng thường xuyên:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
Để phân tích mã máy, trước tiên hãy cấu hình trình phân tích. Xem [hướng dẫn CLI và Evidence](docs/cli.md) về các lệnh phân tích mã máy, lựa chọn trình phân tích, ảnh chụp trạng thái và sử dụng trong script.
|
||||
|
||||
### Cập nhật REA
|
||||
|
||||
REA thay đổi nhanh và các bản phát hành mới thường xuyên có bản sửa lỗi. Hãy giữ bản cài đặt của bạn được cập nhật.
|
||||
|
||||
Với CLI được cài bằng npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Để cập nhật đăng ký tác nhân và skill, chạy lệnh thiết lập được in ra sau khi cập nhật.
|
||||
|
||||
Nếu dùng `npx`, cập nhật thiết lập tác nhân bằng:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Xem lại thay đổi cấu hình và khởi động lại tác nhân. Với lệnh CLI dùng một lần, dùng `npx rea-agents@latest` rồi thêm lệnh cần chạy.
|
||||
|
||||
## Cách REA hoạt động
|
||||
|
||||
Tác nhân gọi REA qua MCP để kiểm tra mục tiêu và lần theo mã liên quan. REA trả về các phát hiện kèm bằng chứng. Tác nhân dùng chúng để đặt câu hỏi tiếp theo, giải thích hành vi hoặc viết và kiểm thử một cách triển khai. Các lệnh CLI sử dụng cùng quy trình.
|
||||
|
||||

|
||||
|
||||
[Mở hình ở kích thước đầy đủ](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## Những gì bạn có thể phân tích
|
||||
|
||||
REA yêu cầu Node.js 22.x (>=22.19), 24.x (>=24.11) hoặc 26+, cùng với npm. Công cụ bổ sung và nền tảng chạy REA được hỗ trợ tùy thuộc vào mục tiêu:
|
||||
|
||||
| Mục tiêu | REA trả về gì | Yêu cầu và hướng dẫn |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Tệp nhị phân mã máy | Mã giả, hợp ngữ, chuỗi, ký hiệu, lời gọi và tham chiếu | Hopper, Ghidra hoặc IDA; [phân tích mã máy](https://rea.tools/guides/native/) |
|
||||
| Bố cục ELF ngoại tuyến | Các section, segment, ký hiệu/thông tin tái định vị gốc và các biện pháp giảm thiểu có thể có từ phân tích tĩnh | pwntools do bên gọi cung cấp trên Linux x64; [chẩn đoán tệp nhị phân](docs/binary-diagnostics.md) |
|
||||
| Bytecode EVM | Bộ chọn điều phối, độ lệch byte, tham số suy luận và khả năng thay đổi trạng thái | Đầu vào cục bộ chứa byte thô hoặc dạng thập lục phân; [hướng dẫn EVM ngoại tuyến](docs/evm-bytecode.md) |
|
||||
| Sự cố Linux đã ghi lại | Bản ghi note thô, thanh ghi/tín hiệu của mọi luồng được ghi lại và các ứng viên ánh xạ tùy chọn | pwntools do bên gọi cung cấp; GDB/pwndbg tùy chọn; [sự cố đã ghi lại](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Mô-đun, các khai báo import, source map, tuyến, IPC và quan hệ với tiện ích mở rộng mã máy | Node.js và npm; [phân tích ứng dụng](https://rea.tools/guides/javascript/) |
|
||||
| Trang web | Cấu trúc trang, script, quan sát mạng và ảnh chụp màn hình được yêu cầu | Trình duyệt thuộc họ Chrome; [phân tích trình duyệt](https://rea.tools/guides/browser/) |
|
||||
| Bản ghi lưu lượng mạng đã lưu | Yêu cầu, phản hồi, payload có thể truy cập và vị trí nguồn | HAR; mitmdump trên Linux cho bản ghi định dạng gốc của mitmproxy; [hướng dẫn bản ghi mạng](docs/web-network-captures.md) |
|
||||
| Assembly .NET | Siêu dữ liệu, lệnh CIL, các phụ thuộc mã máy được khai báo và so sánh bản dựng | Kiểm tra tĩnh; [hướng dẫn mã được quản lý](docs/managed-code-analysis.md) |
|
||||
| APK Android | Khai báo manifest, lớp, phương thức được dịch ngược và tham chiếu | JADX không giao diện đồ họa và JDK đầy đủ trên Linux/macOS; [hướng dẫn Android](docs/android-analysis.md) |
|
||||
| Phần sụn | Các vùng, kết quả trích xuất và dữ liệu chuyển sang phân tích mã máy | Binwalk / Unblob trên Linux; [hướng dẫn phần sụn](docs/firmware-analysis.md) |
|
||||
| Gói và tài nguyên | Danh sách tệp, giá trị băm, plist, cấu trúc bundle Apple và tài nguyên được trích xuất | [Hướng dẫn hiện vật phần mềm và JavaScript](docs/javascript-artifact-reconstruction.md), [ứng dụng Apple](docs/apple-application-analysis.md) |
|
||||
| Hành vi tiến trình | Đầu ra terminal, tương tác, quan sát khi thoát và hệ thống tệp, cùng so sánh các lần chạy | Linux/macOS có PTY gốc; [ghi lại tiến trình](docs/process-capture.md) |
|
||||
|
||||
Kiểm tra tĩnh JavaScript và .NET đọc các tệp được cung cấp mà không chạy ứng dụng. Ghi lại khi chạy sẽ khởi chạy hoặc tương tác với mục tiêu đã chọn bằng quyền người dùng của bạn; từng hướng dẫn mô tả tác động của nó.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Định dạng mã máy và nền tảng chạy REA được hỗ trợ khác nhau tùy trình phân tích. Xem [thiết lập Hopper và Ghidra](docs/installation.md#hopper), [hướng dẫn IDA](docs/ida-provider.md) và [hỗ trợ Ghidra thử nghiệm trên Windows](docs/windows-ghidra-p0.md). Ghidra cũng hỗ trợ [phân tích DOS 16 bit](docs/ghidra-dos.md). Để chọn trình phân tích, xem [hướng dẫn CLI](docs/cli.md#choose-a-provider). Kiểm tra [khả năng sử dụng theo bản phát hành](docs/installation.md#released-package-and-main) đối với tính năng được bổ sung sau phiên bản npm mới nhất.
|
||||
|
||||
## Các nghiên cứu điển hình
|
||||
|
||||
### DX-Ball: tái tạo phép tính phân bố âm thanh trái phải
|
||||
|
||||
Lần theo lời gọi âm thanh đến hàm hỗ trợ chuyển vị trí thành giá trị phân bố âm thanh, kiểm tra các lệnh và chuyển mã giả chưa đầy đủ sang C. Bản tái tạo vượt qua 3.205 trường hợp kiểm thử với x86 gốc và tái tạo đủ 63 byte của hàm đã biên dịch.
|
||||
|
||||
[Đọc nghiên cứu điển hình](https://rea.tools/showcase/dx-ball/) · [Kho mã tái tạo](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: lần theo cầu nối clipboard của Electron
|
||||
|
||||
Tìm API clipboard trong tiến trình kết xuất, lần theo qua preload và IPC đến tiến trình chính, rồi kiểm tra định dạng clipboard có dữ liệu định dạng phong phú.
|
||||
|
||||
[Đọc nghiên cứu điển hình](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04: khôi phục phép tính vòng đạn trong DOS
|
||||
|
||||
Kiểm tra các lệnh 16 bit của trò chơi PC-98 gốc, khôi phục phép tính góc cố định và góc nhắm mục tiêu, rồi so sánh C++ được tái tạo với đầu ra của trình biên dịch thời đó.
|
||||
|
||||
[Đọc nghiên cứu điển hình](https://rea.tools/showcase/th04/) · [Kho mã tái tạo](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
Nếu bạn dùng REA cho một mục tiêu thú vị, chúng tôi muốn biết. Chia sẻ trường hợp của bạn qua [issue](https://github.com/morluto/rea/issues) hoặc [pull request](https://github.com/morluto/rea/pulls), nêu mục tiêu, câu hỏi, cách REA giúp bạn và những gì tìm được.
|
||||
|
||||
## Câu hỏi thường gặp
|
||||
|
||||
<details>
|
||||
<summary><strong>Tác nhân nào có thể dùng REA?</strong></summary>
|
||||
|
||||
Bất kỳ tác nhân nào hỗ trợ máy chủ MCP cục bộ. Quá trình thiết lập cấu hình các [tác nhân được hỗ trợ](docs/installation.md#supported-agents); ứng dụng khách khác có thể dùng [đăng ký MCP thủ công](docs/installation.md#mcp-registry).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Tôi có cần Hopper, Ghidra hoặc IDA không?</strong></summary>
|
||||
|
||||
Phân tích mã máy chuyên sâu sử dụng một trong những công cụ đó. Kiểm tra tĩnh JavaScript và .NET hoạt động mà không cần bộ máy phân tích mã máy. Quá trình thiết lập có thể cài Hopper sau khi được chấp thuận; Ghidra và IDA dùng bản cài đặt sẵn có. Xem [thiết lập trình phân tích](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Tôi có cần khởi chạy Hopper trước không?</strong></summary>
|
||||
|
||||
REA khởi chạy Hopper khi một thao tác cần đến nó. Trên macOS, hộp thoại lần chạy đầu có thể yêu cầu chọn chế độ dùng thử hoặc kích hoạt giấy phép. Xem [khởi động Hopper và khắc phục sự cố](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Cài skill từ skills.sh có tác dụng gì?</strong></summary>
|
||||
|
||||
Skill cung cấp hướng dẫn điều tra cho tác nhân. Dùng `rea setup` để đăng ký máy chủ MCP của REA và cài hướng dẫn tương ứng, sau đó khởi động lại tác nhân. Xem [chỉ cài skill](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA trả về mã gì?</strong></summary>
|
||||
|
||||
Phân tích mã máy trả về mã giả và hợp ngữ. Phân tích JavaScript/Electron khôi phục mô-đun cùng quan hệ giữa chúng. Tác nhân dùng các phát hiện này để viết và kiểm thử cách triển khai; các [nghiên cứu điển hình](#các-nghiên-cứu-điển-hình) cung cấp ví dụ thực hành.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA có tải ứng dụng của tôi lên không?</strong></summary>
|
||||
|
||||
REA phân tích mục tiêu cục bộ. Tác nhân nhận kết quả công cụ, và nhà cung cấp mô hình của tác nhân có chính sách dữ liệu riêng.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Tôi nên làm gì khi gặp lỗi?</strong></summary>
|
||||
|
||||
Hãy cập nhật trước; một bản phát hành gần đây có thể đã sửa lỗi.
|
||||
|
||||
Với CLI được cài bằng npm:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
Với thiết lập tác nhân qua `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Nếu dùng tác nhân, hãy hoàn tất [làm mới thiết lập](#cập-nhật-rea) và khởi động lại nó. Thử lại cùng tác vụ. Nếu vấn đề vẫn còn, [mở issue](https://github.com/morluto/rea/issues) với phiên bản REA, loại mục tiêu, các bước tái hiện và đầu ra lỗi.
|
||||
|
||||
</details>
|
||||
|
||||
## Tài liệu
|
||||
|
||||
Bắt đầu với [hướng dẫn thực hành](https://rea.tools/guides/) trên trang web. Để biết tùy chọn cụ thể, điều kiện tiên quyết và hợp đồng kết quả:
|
||||
|
||||
- [Cài đặt và thiết lập](docs/installation.md): đăng ký tác nhân, cấu hình trình phân tích, cập nhật và gỡ cài đặt.
|
||||
- [Kiểm tra mức sẵn sàng và khắc phục sự cố](docs/installation.md#check-readiness-for-your-task): chẩn đoán một tác nhân hoặc bộ máy phân tích cụ thể.
|
||||
- [CLI và Evidence](docs/cli.md): lệnh, lựa chọn trình phân tích, ảnh chụp trạng thái, nhập/xuất và trạng thái thoát.
|
||||
- [Hợp đồng MCP](docs/mcp-contracts.md) và [prompt cho tác nhân](docs/mcp-prompts.md): kết quả công cụ, phiên làm việc và điều tra có hướng dẫn.
|
||||
- [Danh mục công cụ](docs/mcp-contracts.md#generated-catalog): danh sách công cụ, trình phân tích và lệnh CLI được tạo khi dựng dự án.
|
||||
- [Lộ trình](docs/roadmap.md): công việc dự kiến và theo dõi các khả năng.
|
||||
|
||||
Báo cáo lỗ hổng theo [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Đóng góp
|
||||
|
||||
Chúng tôi hoan nghênh bạn giúp phát triển REA! [Mở issue](https://github.com/morluto/rea/issues) để báo lỗi hoặc đề xuất tính năng, hoặc [gửi pull request](https://github.com/morluto/rea/pulls) để cải thiện mã hay tài liệu.
|
||||
|
||||
Xem [CONTRIBUTING.md](CONTRIBUTING.md) về thiết lập môi trường phát triển và các bước kiểm tra, [hướng dẫn kiểm thử](docs/testing.md) về các luồng xác minh và [sơ đồ kiến trúc](docs/architecture.mermaid) về cấu trúc dự án.
|
||||
|
||||
## Liên kết dự án
|
||||
|
||||
[Trang web](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Bảo mật](SECURITY.md)
|
||||
|
||||
## Lịch sử sao
|
||||
|
||||
🎉 **30.000 sao trên GitHub — xin cảm ơn!**
|
||||
|
||||
Cảm ơn mọi người đã sử dụng REA, báo lỗi, đề xuất tính năng, kiểm thử bản dựng và đóng góp bản sửa lỗi.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="Lịch sử sao GitHub của REA" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Miễn trừ trách nhiệm
|
||||
|
||||
REA cung cấp công cụ cho nghiên cứu dịch ngược, phân tích và tái tạo hợp pháp. Bạn có trách nhiệm có được các quyền cho phép cần thiết và tuân thủ luật áp dụng. Dự án không ủng hộ việc sử dụng trái pháp luật hoặc không được cho phép.
|
||||
|
||||
## Giấy phép
|
||||
|
||||
[MIT](LICENSE)
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · **繁體中文** · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA:逆向分析一切
|
||||
|
||||
### 通過一個 MCP 服務,逆向分析二進位檔案、應用程式和執行階段行為。
|
||||
|
||||
**看到喜歡的功能,弄清它的工作原理,深入到二進位層面。**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[網站](https://rea.tools/) · [指南](https://rea.tools/guides/) · [案例](https://rea.tools/showcase/)**
|
||||
|
||||
[快速開始](#快速開始) · [REA 的工作原理](#rea-的工作原理) · [可以分析什麼](#可以分析什麼) · [案例](#案例) · [常見問題](#常見問題) · [文件](#文件)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA 在 Hopper 中啟動分析橋,檢查原生二進位檔案" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA 社群">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>加入逆向工程社群</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 問答 · 成果分享</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
在應用中看到一個想加入自己產品的功能?讓 AI 代理用 REA 調查它。即使沒有原始碼,AI 代理也可以檢查應用、解釋功能的工作方式、展示證據,並為你的專案實現類似功能。
|
||||
|
||||
REA 將 AI 代理連接到分析工具,用於檢查原生二進位檔案、JavaScript 和 Electron 應用、.NET 程式集以及網站。你也可以在終端中使用同樣的工具。分析在本機執行,結果包含支撐各項結論的證據和相關限制。
|
||||
|
||||
設定流程會向 AI 代理註冊 REA,並安裝對應的工作流程指引。原生分析可以使用已有的 Hopper 或 Ghidra;設定流程也可以在你核准後安裝 Hopper。靜態 JavaScript 分析不需要這兩個引擎。
|
||||
|
||||
> **[訪問 REA 網站](https://rea.tools/)**,查看設定說明、圖解指南和真實案例。
|
||||
|
||||
## 快速開始
|
||||
|
||||
### 設定 AI 代理
|
||||
|
||||
安裝 Node.js 和 npm 後,執行:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
選擇 AI 代理,檢查計劃中的變更並核准。設定流程會添加 REA 的 MCP 服務和對應的工作流程指引,並備份已有組態。完成後重新啟動 AI 代理。
|
||||
|
||||
設定流程支援 Claude Code、Codex、Cursor、Gemini CLI、Grok Build 和[其他 AI 代理](docs/installation.md#supported-agents)。提供者組態和手動註冊 MCP 的方法見[安裝與設定](docs/installation.md)。
|
||||
|
||||
### 詢問 AI 代理
|
||||
|
||||
```text
|
||||
弄清 Notes 應用的搜尋功能如何工作,展示證據,並為我的專案實現類似功能。
|
||||
```
|
||||
|
||||
把 Notes 替換為目標應用,並描述你想瞭解的功能。
|
||||
|
||||
### 使用終端
|
||||
|
||||
檢查已解包的 JavaScript/Electron 應用目錄或 ASAR 檔案:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
結果包含模組、匯入、Electron 邊界及相關證據。將路徑替換為你的目標路徑,例如 Windows 上的 `"D:/apps/example"`。
|
||||
|
||||
如果需要經常使用,可以安裝 `rea` 命令:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
```
|
||||
|
||||
原生分析需要先設定分析提供者。原生命令、提供者選擇、快照和腳本用法見 [CLI 與 Evidence 指南](docs/cli.md)。
|
||||
|
||||
### 更新 REA
|
||||
|
||||
REA 更新較快,新版本經常包含錯誤修復。請將安裝的版本保持最新。
|
||||
|
||||
通過 npm 安裝的 CLI 使用:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
要更新 AI 代理註冊和 skill,請執行更新命令輸出的設定命令。
|
||||
|
||||
如果使用 `npx`,通過以下命令更新 AI 代理設定:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
檢查設定變更並重新啟動 AI 代理。單次 CLI 操作可使用 `npx rea-agents@latest`,後接所需命令。
|
||||
|
||||
## REA 的工作原理
|
||||
|
||||
AI 代理通過 MCP 呼叫 REA,檢查目標並追蹤相關程式碼。REA 返回發現及對應證據。AI 代理據此提出後續問題、解釋行為,或編寫並測試實現。CLI 命令使用同樣的工作流程。
|
||||
|
||||

|
||||
|
||||
[打開完整尺寸的圖示](website/public/assets/figures/rea-investigation-flow.svg)。
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## 可以分析什麼
|
||||
|
||||
REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。額外工具和主機支援取決於分析目標:
|
||||
|
||||
| 目標 | REA 返回的內容 | 要求與指南 |
|
||||
| --------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| 原生二進位檔案 | 偽程式碼、組合語言、字串、符號、呼叫和引用 | Hopper、Ghidra 或 IDA;[原生分析](https://rea.tools/guides/native/) |
|
||||
| 離線 ELF 佈局 | 節、段、原始符號/重定位資訊及靜態防護機制候選項 | Linux x64 上由呼叫方提供的 pwntools;[二進位診斷](docs/binary-diagnostics.md) |
|
||||
| EVM 位元組碼 | 分派選擇器、位元組偏移、推斷的參數和狀態可變性 | 本地原始位元組/十六進位輸入載體;[離線 EVM 指南](docs/evm-bytecode.md) |
|
||||
| 已記錄的 Linux 崩潰 | 原始 note 記錄、每個已記錄執行緒的暫存器/訊號及可選的對映候選項 | 呼叫方提供的 pwntools;可選的 GDB/pwndbg;[已記錄崩潰](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | 模組、匯入、source map、路由、IPC 和原生附加元件關係 | Node.js 和 npm;[應用分析](https://rea.tools/guides/javascript/) |
|
||||
| 網站 | 頁面結構、腳本、網路觀察結果和按請求獲取的螢幕擷取畫面 | Chrome 系瀏覽器;[瀏覽器分析](https://rea.tools/guides/browser/) |
|
||||
| 已保存的網路擷取 | 請求、回應、可訪問的酬載和來源位置 | HAR;原生 mitmproxy 擷取需要 Linux 上的 mitmdump;[擷取指南](docs/web-network-captures.md) |
|
||||
| .NET 程式集 | 中繼資料、CIL 指令、聲明的原生依賴和建置對比 | 靜態檢查;[托管程式碼指南](docs/managed-code-analysis.md) |
|
||||
| Android APK | 清單聲明、類別、反編譯的方法和引用 | Linux/macOS 上的無界面 JADX 和完整 JDK;[Android 指南](docs/android-analysis.md) |
|
||||
| 韌體 | 區域、提取結果和轉交原生分析的內容 | Linux 上的 Binwalk / Unblob;[韌體指南](docs/firmware-analysis.md) |
|
||||
| 軟體包與資源 | 檔案清單、摘要、plist、Apple bundle 結構和提取的資源 | [製品與 JavaScript 指南](docs/javascript-artifact-reconstruction.md)、[Apple 應用](docs/apple-application-analysis.md) |
|
||||
| 處理程序行為 | 終端輸出、互動、退出和檔案系統觀察結果,以及執行對比 | 支援原生 PTY 的 Linux/macOS;[處理程序擷取](docs/process-capture.md) |
|
||||
|
||||
靜態 JavaScript 和 .NET 檢查讀取提供的檔案,不執行應用。執行階段擷取會以你的使用者權限執行目標或與之互動;各執行階段指南說明瞭具體影響。
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
不同提供者支援的原生格式和主機系統有所不同。請參閱 [Hopper 與 Ghidra 設定](docs/installation.md#hopper)、[IDA 指南](docs/ida-provider.md)和[實驗性 Windows Ghidra 支援](docs/windows-ghidra-p0.md)。Ghidra 也支援 [16 位 DOS 分析](docs/ghidra-dos.md)。提供者選擇見 [CLI 指南](docs/cli.md#choose-a-provider)。對於最新 npm 版本發佈後新增的功能,請查看[發佈可用性](docs/installation.md#released-package-and-main)。
|
||||
|
||||
## 案例
|
||||
|
||||
### DX-Ball:重建聲像計算
|
||||
|
||||
沿聲音呼叫追蹤到根據位置計算聲像的輔助函數,檢查指令,將不完整的偽程式碼轉為 C。重建結果通過了 3,205 個原始 x86 測試用例,並復現了編譯後函數的全部 63 個位元組。
|
||||
|
||||
[閱讀案例](https://rea.tools/showcase/dx-ball/) ·
|
||||
[重建專案儲存庫](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion:追蹤 Electron 剪貼簿橋
|
||||
|
||||
找到渲染處理程序的剪貼簿 API,沿 preload 和 IPC 追蹤到主處理程序,並檢查富格式剪貼簿資料。
|
||||
|
||||
[閱讀案例](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04:恢復 DOS 環形子彈的計算
|
||||
|
||||
檢查原始 PC-98 遊戲的 16 位指令,恢復固定角度和瞄準角度的計算,並將重建的 C++ 與當年的編譯器輸出進行對比。
|
||||
|
||||
[閱讀案例](https://rea.tools/showcase/th04/) ·
|
||||
[重建專案儲存庫](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
如果你用 REA 分析了有趣的目標,我們很想瞭解。歡迎通過 [issue](https://github.com/morluto/rea/issues) 或 [pull request](https://github.com/morluto/rea/pulls) 分享案例,說明目標、你的問題、REA 如何提供幫助,以及你的發現。
|
||||
|
||||
## 常見問題
|
||||
|
||||
<details>
|
||||
<summary><strong>哪些 AI 代理可以使用 REA?</strong></summary>
|
||||
|
||||
任何支援本地 MCP 服務的 AI 代理都可以使用。設定流程會設定[受支援的 AI 代理](docs/installation.md#supported-agents);其他客戶端可通過[手動註冊 MCP](docs/installation.md#mcp-registry) 使用。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>需要 Hopper、Ghidra 或 IDA 嗎?</strong></summary>
|
||||
|
||||
深入的原生分析需要其中一個引擎。靜態 JavaScript 和 .NET 檢查不需要原生分析引擎。設定流程可以在你核准後安裝 Hopper;Ghidra 和 IDA 則使用已有安裝。見[提供者設定](docs/installation.md#hopper)。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>需要先啟動 Hopper 嗎?</strong></summary>
|
||||
|
||||
REA 會在操作需要時啟動 Hopper。在 macOS 上,首次執行時可能會出現對話框,讓你選擇演示模式或啟用授權。見 [Hopper 啟動與故障排查](docs/installation.md#launcher-paths-and-troubleshooting)。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>從 skills.sh 安裝 skill 有什麼作用?</strong></summary>
|
||||
|
||||
skill 為 AI 代理提供調查指引。使用 `rea setup` 註冊 REA 的 MCP 服務並安裝對應的指引,然後重新啟動 AI 代理。見[僅安裝 skill](docs/installation.md#skill-only-installation)。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA 返回什麼程式碼?</strong></summary>
|
||||
|
||||
原生分析返回偽程式碼和組合語言。JavaScript/Electron 分析恢復模組及其關係。AI 代理利用這些發現編寫並測試實現;[案例](#案例)提供了完整示例。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA 會上傳我的應用嗎?</strong></summary>
|
||||
|
||||
REA 在本機分析目標。AI 代理會接收工具結果,其模型提供商有自己的資料政策。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>遇到錯誤時該怎麼辦?</strong></summary>
|
||||
|
||||
先更新;最近的版本可能已經修復了問題。
|
||||
|
||||
通過 npm 安裝的 CLI 使用:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
通過 `npx` 設定 AI 代理時使用:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
如果正在使用 AI 代理,請完成[設定更新](#更新-rea)並重新啟動 AI 代理。重試同一任務。如果問題仍然存在,請[提交 issue](https://github.com/morluto/rea/issues),附上 REA 版本、目標類型、復現步驟和錯誤輸出。
|
||||
|
||||
</details>
|
||||
|
||||
## 文件
|
||||
|
||||
可以先閱讀網站上的[實操指南](https://rea.tools/guides/)。具體選項、前提條件和結果契約見:
|
||||
|
||||
- [安裝與設定](docs/installation.md):AI 代理註冊、提供者組態、更新和卸載。
|
||||
- [就緒檢查與故障排查](docs/installation.md#check-readiness-for-your-task):診斷特定 AI 代理或分析引擎。
|
||||
- [CLI 與 Evidence](docs/cli.md):命令、提供者選擇、快照、匯入/匯出和退出狀態。
|
||||
- [MCP 契約](docs/mcp-contracts.md)和[AI 代理提示詞](docs/mcp-prompts.md):工具結果、會話和引導式調查。
|
||||
- [工具目錄](docs/mcp-contracts.md#generated-catalog):建置時生成的工具、提供者和 CLI 命令清單。
|
||||
- [路線圖](docs/roadmap.md):計劃中的工作和能力跟蹤。
|
||||
|
||||
請按照 [SECURITY.md](SECURITY.md) 報告漏洞。
|
||||
|
||||
## 參與貢獻
|
||||
|
||||
歡迎幫助改進 REA!你可以[提交 issue](https://github.com/morluto/rea/issues) 報告錯誤或建議功能,也可以[提交 pull request](https://github.com/morluto/rea/pulls) 改進程式碼或文件。
|
||||
|
||||
開發設定和檢查見 [CONTRIBUTING.md](CONTRIBUTING.md),驗證流程見[測試指南](docs/testing.md),專案結構見[架構圖](docs/architecture.mermaid)。
|
||||
|
||||
## 專案連結
|
||||
|
||||
[網站](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [安全](SECURITY.md)
|
||||
|
||||
## Star 歷史
|
||||
|
||||
🎉 **GitHub Star 達到 30,000 個,感謝大家!**
|
||||
|
||||
感謝每一位使用 REA、報告錯誤、提出功能需求、測試建置和貢獻修復的朋友。
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA 的 GitHub Star 歷史" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 免責聲明
|
||||
|
||||
REA 為合法的逆向工程研究、分析和重建提供工具。你有責任取得所需授權並遵守適用法律。本專案不認同非法或未經授權的使用。
|
||||
|
||||
## 授權條款
|
||||
|
||||
[MIT](LICENSE)
|
||||
+196
-215
@@ -1,23 +1,36 @@
|
||||
<div align="center">
|
||||
|
||||
[English](README.md) · **简体中文** · [日本語](README_ja.md) · [한국어](README_ko.md) · [العربية](README_ar.md)
|
||||
[English](README.md) · **简体中文** · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md)
|
||||
|
||||
# REA:逆向工程一切
|
||||
# REA:逆向分析一切
|
||||
|
||||
### 使用智能体逆向工程任何程序,从应用行为到原生二进制文件。
|
||||
### 通过一个 MCP 服务,逆向分析二进制文件、应用程序和运行时行为。
|
||||
|
||||
**看到喜欢的功能。理解它的原理。按照你的方式实现。**
|
||||
**看到喜欢的功能,弄清它的工作原理,深入到二进制层面。**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#调查工具目录)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[快速开始](#快速开始) · [当前状态](#当前状态) · [从二进制到行为](#从二进制到行为) · [调查工具目录](#调查工具目录) · [路线图](#路线图) · [工作原理](#工作原理)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
<table aria-label="REA community">
|
||||
**[网站](https://rea.tools/) · [指南](https://rea.tools/guides/) · [案例](https://rea.tools/showcase/)**
|
||||
|
||||
[快速开始](#快速开始) · [REA 的工作原理](#rea-的工作原理) · [可以分析什么](#可以分析什么) · [案例](#案例) · [常见问题](#常见问题) · [文档](#文档)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA 在 Hopper 中启动分析桥,检查原生二进制文件" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA 社区">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
@@ -31,275 +44,243 @@
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA 在 Hopper 中启动分析桥并检查原生二进制文件" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
看到某个应用中想加入自己产品的功能?让智能体用 REA 调查它。即使没有源代码,智能体也可以检查应用、解释功能的工作方式、展示证据,并为你的项目实现类似功能。
|
||||
在应用中看到一个想加入自己产品的功能?让智能体用 REA 调查它。即使没有源代码,智能体也可以检查应用、解释功能的工作方式、展示证据,并为你的项目实现类似功能。
|
||||
|
||||
REA 提供原生二进制、JavaScript 与 Electron 应用、.NET 程序集和网站的分析工具。你可以从智能体或终端使用它们。分析在本机运行,结果会说明证据和限制。
|
||||
REA 将智能体连接到分析工具,用于检查原生二进制文件、JavaScript 和 Electron 应用、.NET 程序集以及网站。你也可以在终端中使用同样的工具。分析在本机运行,结果包含支撑各项结论的证据和相关限制。
|
||||
|
||||
Setup 可以配置智能体、连接已安装的 Hopper 或 Ghidra,也可以在你批准后安装 Hopper。
|
||||
设置流程会向智能体注册 REA,并安装匹配的工作流指引。原生分析可以使用已有的 Hopper 或 Ghidra;设置流程也可以在你批准后安装 Hopper。静态 JavaScript 分析不需要这两个引擎。
|
||||
|
||||
## 直接询问智能体
|
||||
|
||||
完成[设置](#快速开始)后,重启智能体并提出问题:
|
||||
|
||||
```text
|
||||
解释“备忘录”应用的搜索功能,展示证据,然后为我的项目实现类似功能。
|
||||
```
|
||||
|
||||
把“备忘录”替换成你想了解的应用,也可以先请求概览。
|
||||
|
||||
## 从二进制到行为
|
||||
|
||||
| 反编译 | 理解 | 重建 |
|
||||
| ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| 打开原生应用或可执行文件,恢复过程、伪代码、汇编、字符串、符号、段和元数据。 | 沿调用者、被调用者、交叉引用和调用图追踪,直到智能体能够解释功能或算法的实际工作方式。 | 将智能体学到的内容变成适合你的技术栈、界面和需求的产品功能。 |
|
||||
|
||||
REA 让调查始终以二进制证据为依据。它不会声称能恢复原始源代码,也不会自动克隆整个应用。
|
||||
|
||||
## 为什么选择 REA
|
||||
|
||||
| | |
|
||||
| ---------------- | -------------------------------------------------------------------- |
|
||||
| **为智能体设计** | 直接询问编译后应用的行为,让智能体搜集证据,而不是猜测。 |
|
||||
| **CLI 与 MCP** | 在终端或编程智能体中使用同一套逆向工程能力。 |
|
||||
| **处理复杂流程** | REA 负责工具设置、打开应用、维持调查过程,并在完成后清理资源。 |
|
||||
| **完整工作流** | 从初步概览推进到伪代码、调用关系、类型和实现线索。 |
|
||||
| **本地运行** | 分析在受支持的本机系统上运行;REA 不会把二进制上传到托管式分析服务。 |
|
||||
| **保留上下文** | 连续调查多个二进制文件,无需为每个问题重新开始整个分析过程。 |
|
||||
> **[访问 REA 网站](https://rea.tools/)**,查看设置说明、图解指南和真实案例。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 运行设置(推荐)
|
||||
### 设置智能体
|
||||
|
||||
配置 REA 与智能体的连接:
|
||||
安装 Node.js 和 npm 后,运行:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Setup 首先让你多选要连接的智能体。已有 REA 注册默认选中;仅被检测到的客户端不会自动选中,也可以手动选择尚未配置的客户端。查看具体路径和变更后再批准。所选智能体默认安装 REA 工作流;Hopper 是单独的可选操作,需要单独批准。Setup 也可以记录现有 Ghidra 的路径。
|
||||
选择智能体,检查计划中的变更并批准。设置流程会添加 REA 的 MCP 服务和匹配的工作流指引,并备份已有配置。完成后重启智能体。
|
||||
|
||||
Setup 会先展示变更并备份已有配置。要求与更多选项见[安装与设置](docs/installation.md)。
|
||||
设置流程支持 Claude Code、Codex、Cursor、Gemini CLI、Grok Build 和[其他智能体](docs/installation.md#supported-agents)。提供方配置和手动注册 MCP 的方法见[安装与设置](docs/installation.md)。
|
||||
|
||||
### 使用智能体
|
||||
|
||||
设置完成后,重启智能体并描述你想了解的应用或功能。REA 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。其他智能体可以使用下方的 MCP 配置。
|
||||
|
||||
Hopper 支持演示模式。如果首次启动时出现提示,选择演示模式或输入已有许可证。
|
||||
|
||||
### 从终端运行
|
||||
|
||||
设置完成后:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
### 安装 rea 命令
|
||||
|
||||
安装命令行工具:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
需要预先安装 Node.js 和 npm。在终端中运行时,安装程序会安装 `rea` 并启动设置。
|
||||
|
||||
也可以用 npm 安装,再运行设置:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 运行要求
|
||||
|
||||
- macOS 12 或更高版本
|
||||
- Ubuntu 24.04+、Fedora 41+ 或 64 位 Arch Linux
|
||||
- Node.js 22.19+ 或 24.11+,以及 npm
|
||||
|
||||
原生二进制分析需要 Hopper 或 Ghidra。Hopper 是独立软件;演示模式有厂商规定的限制,不要求购买许可证。
|
||||
|
||||
Ghidra 支持 Linux x64 和 macOS x64/arm64。单独安装 Ghidra 12.1.4 和完整的 64 位 JDK 21,然后配置 REA 使用它们。macOS 还需要对应架构的原生反编译器。
|
||||
|
||||
Setup 可以验证安装并保存路径,不会安装或升级 Ghidra、Java、Node.js、npm 或 Homebrew。
|
||||
|
||||
Windows Ghidra 分析目前不可用。进程归属、私有目录权限和安全路径检查尚未实现,正确安装 Ghidra 和 Java 也不能启用分析。详见 [Windows Ghidra P0](docs/windows-ghidra-p0.md)。
|
||||
|
||||
### 故障排查
|
||||
|
||||
运行 `npx -y rea-agents@latest doctor`,检查主机、依赖、分析工具和智能体配置。该命令不会修改文件。添加 `--json` 可获取结构化诊断。
|
||||
|
||||
Linux 上的默认 Hopper 启动器为 `/opt/hopper/bin/Hopper`。其他路径可通过 `HOPPER_LAUNCHER_PATH` 指定。如果文件存在但仍报告缺少分析引擎,运行 `ldd /opt/hopper/bin/Hopper | grep 'not found'` 检查缺少的系统库。安装详情见 [Hopper 指南](docs/installation.md#hopper)。
|
||||
|
||||
### 更新与卸载
|
||||
|
||||
- `rea update` 更新当前 REA 安装。
|
||||
- `rea uninstall` 移除 REA 管理的智能体配置项和工作流文件,保留 Hopper。
|
||||
- `rea uninstall --purge-data` 还会删除 REA 的缓存和状态。仅在需要移除这些数据时使用。
|
||||
|
||||
## 当前状态
|
||||
|
||||
REA 可以通过 CLI 和 MCP 分析原生二进制、JavaScript/Electron 应用、.NET 程序集和网站。具体操作取决于主机、目标和所选分析工具。
|
||||
|
||||
- Hopper 支持原生分析和注释操作;GUI 行为取决于平台。
|
||||
- Ghidra 在 Linux x64 和 macOS x64/arm64 上提供 22 项只读操作,包括清单、搜索、反编译、汇编、调用关系、引用、指令与类型检查。它不提供 GUI 或修改操作。
|
||||
- 浏览器、Electron 和进程运行时工作流有独立的配置、批准和生命周期要求。详见 [English README](README.md#current-status) 中的完整状态说明。
|
||||
- Windows Ghidra 分析尚未启用,跟踪进展见 [#527](https://github.com/morluto/rea/issues/527)。
|
||||
|
||||
## 一个提示词,完成一次完整调查
|
||||
### 询问智能体
|
||||
|
||||
```text
|
||||
逆向工程“备忘录”应用,找到离线搜索功能的工作方式,解释其控制流,
|
||||
并使用 TypeScript 和 SQLite 为我的项目构建一个版本。
|
||||
弄清 Notes 应用的搜索功能如何工作,展示证据,并为我的项目实现类似功能。
|
||||
```
|
||||
|
||||
| 步骤 | 智能体的操作 | REA 工具 |
|
||||
| ---: | ---------------------- | ---------------------------------------------------------------- |
|
||||
| 1 | 打开并识别二进制文件 | `open_binary`, `binary_overview` |
|
||||
| 2 | 搜索可能的离线搜索线索 | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | 将线索连接到可执行代码 | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | 重建相关控制流 | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | 反编译相关程序 | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | 在你的项目中构建该功能 | 适合你的技术栈、产品和需求的代码 |
|
||||
把 Notes 替换为目标应用,并描述你想了解的功能。
|
||||
|
||||
REA 负责第 1–5 步中的二进制分析。第 6 步由智能体使用其常规文件编辑与测试工具完成。
|
||||
### 使用终端
|
||||
|
||||
## 智能体可以完成什么
|
||||
|
||||
- 在没有源代码时解释某项功能的实现方式。
|
||||
- 重建应用的身份验证、存储、更新或网络流程。
|
||||
- 恢复足够的结构,以记录未公开的格式或接口。
|
||||
- 从字符串或符号追踪到实现可疑行为的代码。
|
||||
- 在一个会话中切换两个应用版本并比较实现路径。
|
||||
- 调查你喜欢的功能,并为自己的产品构建量身定制的版本。
|
||||
- 将恢复的行为转换为产品功能、测试、迁移说明、移植代码或互操作替代品。
|
||||
- 分析 Swift 和 Objective-C 元数据。
|
||||
- 在 Hopper 中留下名称、注释与书签,使人与智能体的分析互相增强。
|
||||
|
||||
## 调查工具目录
|
||||
|
||||
| 工具类别 | 数量 | 用途 |
|
||||
| ----------------- | ---: | --------------------------------------------------------- |
|
||||
| 二进制检查 | 39 | 函数、伪代码、汇编、字符串、符号、段、引用与注释 |
|
||||
| 组合分析 | 14 | 概览、函数分析、批量反编译、调用图、Swift 与 ObjC 检查 |
|
||||
| macOS 原生工具 | 7 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原 |
|
||||
| 制品检查 | 5 | 目录与软件包检查、Interface Builder、Apple 资源目录与提取 |
|
||||
| .NET PE/CLI | 7 | 程序集身份、元数据、CIL、原生调用声明、成员比较与重建导入 |
|
||||
| 浏览器观察 | 9 | 页面、脚本、来源映射、WebMCP 工具、截图与捕获比较 |
|
||||
| Electron 分析 | 5 | 页面观察、应用结构与静态/运行时结果关联 |
|
||||
| JavaScript 运行时 | 2 | 连接现有 Node/Electron Inspector,观察脚本与执行上下文 |
|
||||
| 应用工作流 | 7 | 功能追踪、版本比较、返回结构比较与重建验证 |
|
||||
| 二进制会话 | 21 | 目标切换、证据包、进程与函数比较、待解决问题记录 |
|
||||
|
||||
## 路线图
|
||||
|
||||
接下来的工作包括更多原生目标验证、JavaScript 与 .NET 跨版本比较,以及运行时观察和重建验证。当前设置已支持选择智能体集成和 Hopper 安装;安装其他分析工具仍是后续工作。详见[安装路线图](docs/roadmap.md)和[分析工具评估](docs/provider-evaluation.md)。
|
||||
|
||||
## 与其他编程智能体一起使用
|
||||
|
||||
Setup 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。任何支持本地 MCP 服务器的智能体都可以使用以下配置连接 REA。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@4.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## 工作原理
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["编程智能体"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["终端"] --> REA
|
||||
REA --> Hopper["Hopper 分析操作"]
|
||||
Hopper --> App["你的应用"]
|
||||
REA --> Ghidra["Ghidra 只读分析<br/>清单 + 函数分析"]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI 与 MCP 服务器使用相同的分析流程。终端命令完成后释放自己创建的桥接会话;智能体会话可以在调查期间保持连接。关闭 REA 会话不会退出用户正在使用的 Hopper 应用。
|
||||
|
||||
## CLI
|
||||
|
||||
上面的智能体工作流是使用 REA 最简单的方式。如果只想在终端中快速了解一个应用:
|
||||
检查已解包的 JavaScript/Electron 应用目录或 ASAR 文件:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
运行 `npx -y rea-agents@latest --help` 查看直接反编译和其他选项。
|
||||
结果包含模块、导入、Electron 边界及相关证据。将路径替换为你的目标路径,例如 Windows 上的 `"D:/apps/example"`。
|
||||
|
||||
也可以全局安装 `rea` 命令:
|
||||
如果需要经常使用,可以安装 `rea` 命令:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA 可以直接打开 Mac 的 `.app` 文件夹。如果智能体找不到应用,请告诉它应用安装在哪里。
|
||||
原生分析需要先配置分析提供方。原生命令、提供方选择、快照和脚本用法见 [CLI 与 Evidence 指南](docs/cli.md)。
|
||||
|
||||
## Hopper 应用行为
|
||||
### 更新 REA
|
||||
|
||||
REA 会在需要时启动 Hopper,无需预先运行。Hopper 启动器内部会激活应用,因此打开目标时 Hopper 可能出现在其他窗口前。REA 会请求 macOS 在后台启动 Hopper,但无法保证窗口始终位于后台。
|
||||
REA 更新较快,新版本经常包含错误修复。请保持安装的版本为最新。
|
||||
|
||||
REA 会推导明确的格式和架构参数,以避免常见的 FAT 与 ARM 选择对话框。其他 Hopper 或 macOS 对话框仍可能需要人工响应。关闭 REA 会话会终止桥并删除私有套接字目录,但不会退出用户正在使用的 Hopper 应用。
|
||||
通过 npm 安装的 CLI 使用:
|
||||
|
||||
## 安全模型
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
每个桥会话都使用随机能力令牌和仅限当前用户的 Unix 套接字。诊断保留本机路径、摘要和错误位置等排查信息,同时移除凭据和认证令牌。Ghidra 会话还使用隔离的临时项目,不会打开或修改用户自己的 Ghidra 项目。
|
||||
要更新智能体注册和 skill,请运行更新命令输出的设置命令。
|
||||
|
||||
这不是沙箱,也无法防御以同一操作系统用户身份运行的恶意进程。打开不可信二进制文件会让所选本地提供商以当前用户权限进行解析和分析。请按照 [SECURITY.md](SECURITY.md) 中的私密流程报告漏洞。
|
||||
如果使用 `npx`,通过以下命令更新智能体设置:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
检查设置变更并重启智能体。单次 CLI 操作可使用 `npx rea-agents@latest`,后接所需命令。
|
||||
|
||||
## REA 的工作原理
|
||||
|
||||
智能体通过 MCP 调用 REA,检查目标并追踪相关代码。REA 返回发现及对应证据。智能体据此提出后续问题、解释行为,或编写并测试实现。CLI 命令使用同样的工作流。
|
||||
|
||||

|
||||
|
||||
[打开完整尺寸的图示](website/public/assets/figures/rea-investigation-flow.svg)。
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## 可以分析什么
|
||||
|
||||
REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。额外工具和主机支持取决于分析目标:
|
||||
|
||||
| 目标 | REA 返回的内容 | 要求与指南 |
|
||||
| --------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| 原生二进制文件 | 伪代码、汇编、字符串、符号、调用和引用 | Hopper、Ghidra 或 IDA;[原生分析](https://rea.tools/guides/native/) |
|
||||
| 离线 ELF 布局 | 节、段、原始符号/重定位信息及静态防护机制候选项 | Linux x64 上由调用方提供的 pwntools;[二进制诊断](docs/binary-diagnostics.md) |
|
||||
| EVM 字节码 | 分派选择器、字节偏移、推断的参数和状态可变性 | 本地原始字节/十六进制输入载体;[离线 EVM 指南](docs/evm-bytecode.md) |
|
||||
| 已记录的 Linux 崩溃 | 原始 note 记录、每个已记录线程的寄存器/信号及可选的映射候选项 | 调用方提供的 pwntools;可选的 GDB/pwndbg;[已记录崩溃](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | 模块、导入、source map、路由、IPC 和原生扩展关系 | Node.js 和 npm;[应用分析](https://rea.tools/guides/javascript/) |
|
||||
| 网站 | 页面结构、脚本、网络观察结果和按请求获取的截图 | Chrome 系浏览器;[浏览器分析](https://rea.tools/guides/browser/) |
|
||||
| 已保存的网络捕获 | 请求、响应、可访问的载荷和来源位置 | HAR;原生 mitmproxy 捕获需要 Linux 上的 mitmdump;[捕获指南](docs/web-network-captures.md) |
|
||||
| .NET 程序集 | 元数据、CIL 指令、声明的原生依赖和构建对比 | 静态检查;[托管代码指南](docs/managed-code-analysis.md) |
|
||||
| Android APK | 清单声明、类、反编译的方法和引用 | Linux/macOS 上的无界面 JADX 和完整 JDK;[Android 指南](docs/android-analysis.md) |
|
||||
| 固件 | 区域、提取结果和转交原生分析的内容 | Linux 上的 Binwalk / Unblob;[固件指南](docs/firmware-analysis.md) |
|
||||
| 软件包与资源 | 文件清单、摘要、plist、Apple bundle 结构和提取的资源 | [制品与 JavaScript 指南](docs/javascript-artifact-reconstruction.md)、[Apple 应用](docs/apple-application-analysis.md) |
|
||||
| 进程行为 | 终端输出、交互、退出和文件系统观察结果,以及运行对比 | 支持原生 PTY 的 Linux/macOS;[进程捕获](docs/process-capture.md) |
|
||||
|
||||
静态 JavaScript 和 .NET 检查读取提供的文件,不运行应用。运行时捕获会以你的用户权限运行目标或与之交互;各运行时指南说明了具体影响。
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
不同提供方支持的原生格式和主机系统有所不同。请参阅 [Hopper 与 Ghidra 设置](docs/installation.md#hopper)、[IDA 指南](docs/ida-provider.md)和[实验性 Windows Ghidra 支持](docs/windows-ghidra-p0.md)。Ghidra 也支持 [16 位 DOS 分析](docs/ghidra-dos.md)。提供方选择见 [CLI 指南](docs/cli.md#choose-a-provider)。对于最新 npm 版本发布后新增的功能,请查看[发布可用性](docs/installation.md#released-package-and-main)。
|
||||
|
||||
## 案例
|
||||
|
||||
### DX-Ball:重建声像计算
|
||||
|
||||
沿声音调用追踪到根据位置计算声像的辅助函数,检查指令,将不完整的伪代码转为 C。重建结果通过了 3,205 个原始 x86 测试用例,并复现了编译后函数的全部 63 个字节。
|
||||
|
||||
[阅读案例](https://rea.tools/showcase/dx-ball/) ·
|
||||
[重建项目仓库](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion:追踪 Electron 剪贴板桥
|
||||
|
||||
找到渲染进程的剪贴板 API,沿 preload 和 IPC 追踪到主进程,并检查富格式剪贴板数据。
|
||||
|
||||
[阅读案例](https://rea.tools/showcase/notion/)
|
||||
|
||||
### TH04:恢复 DOS 环形子弹的计算
|
||||
|
||||
检查原始 PC-98 游戏的 16 位指令,恢复固定角度和瞄准角度的计算,并将重建的 C++ 与当年的编译器输出进行对比。
|
||||
|
||||
[阅读案例](https://rea.tools/showcase/th04/) ·
|
||||
[重建项目仓库](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
如果你用 REA 分析了有趣的目标,我们很想了解。欢迎通过 [issue](https://github.com/morluto/rea/issues) 或 [pull request](https://github.com/morluto/rea/pulls) 分享案例,说明目标、你的问题、REA 如何提供帮助,以及你的发现。
|
||||
|
||||
## 常见问题
|
||||
|
||||
<details><summary><strong>Hopper 是否需要提前运行?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>哪些智能体可以使用 REA?</strong></summary>
|
||||
|
||||
不需要。REA 会在操作需要时启动 Hopper。Hopper 已经运行时也可以使用,但 REA 会打开新的分析文档,不会连接并复用现有 GUI 文档。
|
||||
任何支持本地 MCP 服务的智能体都可以使用。设置流程会配置[受支持的智能体](docs/installation.md#supported-agents);其他客户端可通过[手动注册 MCP](docs/installation.md#mcp-registry) 使用。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA 是否包含 Hopper?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>需要 Hopper、Ghidra 或 IDA 吗?</strong></summary>
|
||||
|
||||
不包含。Setup 可以为你安装 Hopper,但 Hopper 仍是需要单独授权的软件。REA 提供 CLI、MCP 服务器和面向智能体的工作流。
|
||||
深入的原生分析需要其中一个引擎。静态 JavaScript 和 .NET 检查不需要原生分析引擎。设置流程可以在你批准后安装 Hopper;Ghidra 和 IDA 则使用已有安装。见[提供方设置](docs/installation.md#hopper)。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA 会上传我的二进制文件吗?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>需要先启动 Hopper 吗?</strong></summary>
|
||||
|
||||
REA 不提供托管分析服务,而是通过本地 Unix 套接字把操作交给 Hopper。你的智能体或模型服务商可能有自己的数据政策,请单独核查。
|
||||
REA 会在操作需要时启动 Hopper。在 macOS 上,首次运行时可能会出现对话框,让你选择演示模式或激活许可证。见 [Hopper 启动与故障排查](docs/installation.md#launcher-paths-and-troubleshooting)。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA 能恢复原始源代码吗?</strong></summary>
|
||||
<details>
|
||||
<summary><strong>从 skills.sh 安装 skill 有什么作用?</strong></summary>
|
||||
|
||||
不能保证。REA 提供伪代码、汇编、符号、字符串、元数据和关系,智能体可据此解释或兼容地重建观察到的行为。
|
||||
skill 为智能体提供调查指引。使用 `rea setup` 注册 REA 的 MCP 服务并安装匹配的指引,然后重启智能体。见[仅安装 skill](docs/installation.md#skill-only-installation)。
|
||||
|
||||
</details>
|
||||
|
||||
## 开发
|
||||
<details>
|
||||
<summary><strong>REA 返回什么代码?</strong></summary>
|
||||
|
||||
开发环境、架构、测试和发布说明请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
原生分析返回伪代码和汇编。JavaScript/Electron 分析恢复模块及其关系。智能体利用这些发现编写并测试实现;[案例](#案例)提供了完整示例。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>REA 会上传我的应用吗?</strong></summary>
|
||||
|
||||
REA 在本机分析目标。智能体会接收工具结果,其模型提供商有自己的数据政策。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>遇到错误时该怎么办?</strong></summary>
|
||||
|
||||
先更新;最近的版本可能已经修复了问题。
|
||||
|
||||
通过 npm 安装的 CLI 使用:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
通过 `npx` 设置智能体时使用:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
如果正在使用智能体,请完成[设置更新](#更新-rea)并重启智能体。重试同一任务。如果问题仍然存在,请[提交 issue](https://github.com/morluto/rea/issues),附上 REA 版本、目标类型、复现步骤和错误输出。
|
||||
|
||||
</details>
|
||||
|
||||
## 文档
|
||||
|
||||
可以先阅读网站上的[实操指南](https://rea.tools/guides/)。具体选项、前提条件和结果契约见:
|
||||
|
||||
- [安装与设置](docs/installation.md):智能体注册、提供方配置、更新和卸载。
|
||||
- [就绪检查与故障排查](docs/installation.md#check-readiness-for-your-task):诊断特定智能体或分析引擎。
|
||||
- [CLI 与 Evidence](docs/cli.md):命令、提供方选择、快照、导入/导出和退出状态。
|
||||
- [MCP 契约](docs/mcp-contracts.md)和[智能体提示词](docs/mcp-prompts.md):工具结果、会话和引导式调查。
|
||||
- [工具目录](docs/mcp-contracts.md#generated-catalog):构建时生成的工具、提供方和 CLI 命令清单。
|
||||
- [路线图](docs/roadmap.md):计划中的工作和能力跟踪。
|
||||
|
||||
请按照 [SECURITY.md](SECURITY.md) 报告漏洞。
|
||||
|
||||
## 参与贡献
|
||||
|
||||
欢迎帮助改进 REA!你可以[提交 issue](https://github.com/morluto/rea/issues) 报告错误或建议功能,也可以[提交 pull request](https://github.com/morluto/rea/pulls) 改进代码或文档。
|
||||
|
||||
开发设置和检查见 [CONTRIBUTING.md](CONTRIBUTING.md),验证流程见[测试指南](docs/testing.md),项目结构见[架构图](docs/architecture.mermaid)。
|
||||
|
||||
## 项目链接
|
||||
|
||||
[网站](https://rea.tools/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [安全](SECURITY.md)
|
||||
|
||||
## Star 历史
|
||||
|
||||
🎉 **GitHub Star 达到 30,000 个,感谢大家!**
|
||||
|
||||
感谢每一位使用 REA、报告错误、提出功能需求、测试构建和贡献修复的朋友。
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA 的 GitHub Star 历史" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 免责声明
|
||||
|
||||
REA 为合法的逆向工程研究、分析和重建提供工具。你有责任取得所需授权并遵守适用法律。项目不支持非法或未经授权的使用。
|
||||
|
||||
## 许可证
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
import com.atxx.jhmcp.*;
|
||||
import com.google.gson.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.*;
|
||||
import jadx.api.*;
|
||||
import jadx.core.dex.nodes.*;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
import kotlin.Unit;
|
||||
import kotlin.coroutines.EmptyCoroutineContext;
|
||||
import kotlinx.coroutines.BuildersKt;
|
||||
import kotlinx.io.CoreKt;
|
||||
import kotlinx.io.JvmCoreKt;
|
||||
import kotlinx.serialization.json.JsonObject;
|
||||
|
||||
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
|
||||
class ReaJadxBridge {
|
||||
private static final Gson JSON = new Gson();
|
||||
private final SessionHolder holder;
|
||||
private JadxSession indexedSession;
|
||||
private Map<String, List<ClassSnapshot>> classes = Map.of();
|
||||
|
||||
private record MethodSnapshot(String name, String shortId, Map<String, Object> summary) {}
|
||||
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
|
||||
|
||||
private static final class InputFailure extends IllegalArgumentException {
|
||||
final String field;
|
||||
InputFailure(String field, String message) { super(message); this.field = field; }
|
||||
}
|
||||
|
||||
private ReaJadxBridge(SessionHolder holder) { this.holder = holder; }
|
||||
|
||||
private static CallToolResult text(String value, boolean failed) {
|
||||
return new CallToolResult(List.of(new TextContent(value, null, null)), failed, null, null);
|
||||
}
|
||||
|
||||
private static CallToolResult json(Object value) { return text(JSON.toJson(value), false); }
|
||||
|
||||
private static String string(JsonObject args, String name) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
|
||||
throw new IllegalArgumentException(name + " must be a string");
|
||||
return value.getAsString();
|
||||
}
|
||||
|
||||
private static int integer(JsonObject args, String name, int fallback) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null) return fallback;
|
||||
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber())
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer");
|
||||
try {
|
||||
int result = value.getAsBigDecimal().intValueExact();
|
||||
if (result < 0) throw new ArithmeticException();
|
||||
return result;
|
||||
} catch (ArithmeticException error) {
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer", error);
|
||||
}
|
||||
}
|
||||
|
||||
private JadxSession session() {
|
||||
JadxSession current = holder.current();
|
||||
if (current == null) throw new IllegalArgumentException("No APK loaded");
|
||||
return current;
|
||||
}
|
||||
|
||||
private ClassSnapshot selected(JsonObject args) {
|
||||
JadxSession current = session();
|
||||
if (indexedSession != current) {
|
||||
Map<String, List<ClassSnapshot>> index = new LinkedHashMap<>();
|
||||
if (!current.getClasses().isEmpty()) {
|
||||
JadxDecompiler decompiler = current.getClasses().get(0).getClassNode().root().getDecompiler();
|
||||
// JavaClass.getMethods/getFields/getInnerClasses all load code. Raw nodes do not.
|
||||
// Snapshot every identity before code generation can rewrite aliases or members.
|
||||
for (JavaClass cls : decompiler.getClassesWithInners()) {
|
||||
ClassNode node = cls.getClassNode();
|
||||
List<MethodNode> ordered = new ArrayList<>(node.getMethods());
|
||||
ordered.sort(Comparator.comparing(MethodNode::getAlias));
|
||||
List<MethodSnapshot> methods = ordered.stream().map(method -> {
|
||||
String name = method.getAlias();
|
||||
String signature = name + "(" + method.getArgTypes().stream()
|
||||
.map(Object::toString).collect(Collectors.joining(", "))
|
||||
+ "): " + method.getReturnType().toString();
|
||||
return new MethodSnapshot(name, method.getMethodInfo().getShortId(), Map.of(
|
||||
"name", name, "signature", signature,
|
||||
"is_constructor", method.isConstructor(), "def_pos", 0));
|
||||
}).toList();
|
||||
List<Map<String, Object>> fields = node.getFields().stream()
|
||||
.sorted(Comparator.comparing(FieldNode::getAlias))
|
||||
.map(field -> Map.<String, Object>of("name", field.getAlias(),
|
||||
"type", field.getType().toString(), "def_pos", 0)).toList();
|
||||
List<String> inner = node.getInnerClasses().stream().map(ClassNode::getFullName).toList();
|
||||
Map<String, Object> summary = Map.of(
|
||||
"full_name", cls.getFullName(), "name", cls.getName(),
|
||||
"method_count", methods.size(), "field_count", fields.size(),
|
||||
"inner_class_count", inner.size(), "inner_classes", inner,
|
||||
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", fields);
|
||||
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>())
|
||||
.add(new ClassSnapshot(cls, methods, summary));
|
||||
}
|
||||
}
|
||||
classes = Collections.unmodifiableMap(index);
|
||||
indexedSession = current;
|
||||
}
|
||||
String name = string(args, "class_name");
|
||||
List<ClassSnapshot> candidates = classes.get(name);
|
||||
if (candidates == null) throw new InputFailure("class_name", "Exact class not found: " + name
|
||||
+ "; use search_android_classes to discover fully qualified names.");
|
||||
if (candidates.size() != 1) throw new InputFailure("class_name", "Ambiguous metadata class identity: " + name
|
||||
+ "; raw identities: " + candidates.stream().map(candidate -> candidate.node().getRawName()).toList());
|
||||
return candidates.get(0);
|
||||
}
|
||||
|
||||
private List<MethodSnapshot> methods(ClassSnapshot cls, JsonObject args) {
|
||||
String name = string(args, "method_name");
|
||||
List<MethodSnapshot> result = cls.methods().stream().filter(method -> method.name().equals(name)).toList();
|
||||
if (result.isEmpty()) throw new InputFailure("method_name", "Method not found: " + name
|
||||
+ "; use inspect_android_class to discover available methods.");
|
||||
return result;
|
||||
}
|
||||
|
||||
private JavaMethod method(ClassSnapshot cls, MethodSnapshot selected) {
|
||||
JavaMethod result = cls.node().searchMethodByShortId(selected.shortId());
|
||||
if (result == null) throw new IllegalArgumentException("Method identity no longer available: " + selected.shortId());
|
||||
return result;
|
||||
}
|
||||
|
||||
private CallToolResult handle(String name, JsonObject args) {
|
||||
if (name.equals("rea_jvm_status")) return json(Map.of(
|
||||
"engine_reported_version", BuildInfo.VERSION,
|
||||
"max_heap_bytes", Runtime.getRuntime().maxMemory(),
|
||||
"available_processors", Runtime.getRuntime().availableProcessors(),
|
||||
"java_version", System.getProperty("java.version"),
|
||||
"metadata_scope", "parsed_members"));
|
||||
JadxSession current = session();
|
||||
if (name.equals("list_classes")) {
|
||||
List<String> names = current.getClassFqns();
|
||||
int offset = integer(args, "offset", 0);
|
||||
int limit = integer(args, "limit", 200);
|
||||
int start = Math.min(offset, names.size());
|
||||
int end = (int) Math.min((long) start + limit, names.size());
|
||||
return json(Map.of("total", names.size(), "offset", offset, "limit", limit,
|
||||
"items", names.subList(start, end)));
|
||||
}
|
||||
ClassSnapshot cls = selected(args);
|
||||
if (name.equals("get_class_summary")) return json(cls.summary());
|
||||
if (name.equals("get_method_body")) {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
int index = integer(args, "overload_index", 0);
|
||||
if (index >= overloads.size()) throw new InputFailure("overload_index", "overload_index out of range: " + index);
|
||||
JavaMethod method = method(cls, overloads.get(index));
|
||||
JadxSession.SmartCode code = current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
|
||||
return json(Map.of("class_name", cls.summary().get("full_name"),
|
||||
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
|
||||
"overload_index", index, "overload_count", overloads.size(),
|
||||
"mode", code.getKind(), "fell_back", code.getFellBack(),
|
||||
"markers", code.getMarkers(), "body", code.getText()));
|
||||
}
|
||||
List<JavaNode> uses;
|
||||
String target = string(args, "class_name");
|
||||
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
|
||||
else {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
if (overloads.size() != 1) throw new InputFailure("method_name", "Method references require a unique overload");
|
||||
target += "." + overloads.get(0).name();
|
||||
uses = method(cls, overloads.get(0)).getUseIn();
|
||||
}
|
||||
// REA consumes all references without materializing source line information.
|
||||
List<Map<String, Object>> items = uses.stream().map(node -> current.describeUsage(node, false)).toList();
|
||||
return json(Map.of("target", target, "total", uses.size(), "count", items.size(),
|
||||
"resolve_line", false, "items", items));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PrintStream protocolOut = System.out;
|
||||
System.setOut(System.err);
|
||||
SessionHolder holder = new SessionHolder(new SessionConfig(
|
||||
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL));
|
||||
ServerCapabilities capabilities = new ServerCapabilities(
|
||||
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
|
||||
Server server = new Server(new Implementation("rea-jadx-bridge", "1", null, null, null),
|
||||
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
|
||||
SessionToolsKt.registerSessionTools(server, holder);
|
||||
ManifestToolsKt.registerManifestTools(server, holder);
|
||||
ClassToolsKt.registerClassTools(server, holder);
|
||||
MethodToolsKt.registerMethodTools(server, holder);
|
||||
XrefToolsKt.registerXrefTools(server, holder);
|
||||
ReaJadxBridge bridge = new ReaJadxBridge(holder);
|
||||
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
|
||||
"list_classes", "get_class_summary", "get_method_body",
|
||||
"get_xrefs_to_class", "get_xrefs_to_method");
|
||||
for (String name : new ArrayList<>(server.getTools().keySet()))
|
||||
if (!consumed.contains(name)) server.removeTool(name);
|
||||
Tool diagnostics = new Tool("rea_jvm_status",
|
||||
new ToolSchema(null, new JsonObject(Map.of()), List.of(), null),
|
||||
"Observed worker JVM and metadata scope", null, null, null, null, null, null);
|
||||
server.addTool(diagnostics, (connection, request, continuation) -> bridge.handle("rea_jvm_status", request.getArguments()));
|
||||
for (String name : List.of("list_classes", "get_class_summary",
|
||||
"get_method_body", "get_xrefs_to_class", "get_xrefs_to_method")) {
|
||||
Tool tool = server.getTools().get(name).getTool();
|
||||
server.removeTool(name);
|
||||
List<String> fields = switch (name) {
|
||||
case "list_classes" -> List.of("offset", "limit");
|
||||
case "get_method_body" -> List.of("class_name", "method_name", "overload_index");
|
||||
case "get_xrefs_to_method" -> List.of("class_name", "method_name");
|
||||
default -> List.of("class_name");
|
||||
};
|
||||
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
|
||||
for (String field : fields) properties.put(field, tool.getInputSchema().getProperties().get(field));
|
||||
String description = switch (name) {
|
||||
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
|
||||
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
|
||||
case "get_method_body" -> "Source for an exact class and stable same-name overload index, with smali fallback.";
|
||||
default -> "Complete static references to an exact class or unique method; source lines are unresolved.";
|
||||
};
|
||||
Tool narrowed = new Tool(name, new ToolSchema(null, new JsonObject(properties),
|
||||
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
|
||||
server.addTool(narrowed, (connection, request, continuation) -> {
|
||||
try { return bridge.handle(name, request.getArguments()); }
|
||||
catch (InputFailure error) { return text(JSON.toJson(Map.of(
|
||||
"kind", "invalid-input", "field", error.field, "message", error.getMessage())), true); }
|
||||
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
|
||||
});
|
||||
}
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
server.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
StdioServerTransport transport = new StdioServerTransport(
|
||||
CoreKt.buffered(JvmCoreKt.asSource(System.in)),
|
||||
CoreKt.buffered(JvmCoreKt.asSink(protocolOut)));
|
||||
transport.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
ServerSession connection = null;
|
||||
try {
|
||||
connection = BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.createSession(transport, continuation));
|
||||
done.await();
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
ServerSession connected = connection;
|
||||
// Unsubscribe the connection before the SDK joins its notification service.
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> connected.close(continuation));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.close(continuation));
|
||||
} finally {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> holder.unload(continuation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,8 @@ import ghidra.app.decompiler.ClangCaseToken;
|
||||
import ghidra.app.decompiler.ClangNode;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressIterator;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.AddressRangeIterator;
|
||||
@@ -73,6 +75,11 @@ import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.InstructionIterator;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlockSourceInfo;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.SegmentedAddress;
|
||||
import ghidra.program.model.reloc.Relocation;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
@@ -112,6 +119,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
"params"
|
||||
);
|
||||
private static final String[] CAPABILITIES = {
|
||||
"annotate_native_function",
|
||||
"inspect_native_load_image",
|
||||
"read_bytes",
|
||||
"address_to_file_offset",
|
||||
"ping",
|
||||
"shutdown",
|
||||
"address_name",
|
||||
@@ -142,6 +153,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
private List<FunctionEntry> procedureInventory;
|
||||
private List<InventoryItem> stringInventory;
|
||||
private DecompInterface decompiler;
|
||||
private final ReaGhidraExtensions analysisExtensions = new ReaGhidraExtensions();
|
||||
private static AddressSpace sessionDefaultAddressSpace;
|
||||
|
||||
@Override
|
||||
@@ -152,12 +164,24 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
Path descriptorPath = Path.of(arguments[0]);
|
||||
SessionDescriptor descriptor = readDescriptor(descriptorPath);
|
||||
Files.deleteIfExists(descriptorPath);
|
||||
// Windows retains a native immutable bearer descriptor lease until
|
||||
// runtime_close; POSIX removes it after this read. Normal cleanup removes
|
||||
// the Windows file, while abrupt owner death can leave private residue.
|
||||
// Its protected runtime DACL also protects the bearer token while the
|
||||
// bridge is running. POSIX keeps its existing consume-and-delete flow.
|
||||
if (!isWindowsHost()) {
|
||||
Files.deleteIfExists(descriptorPath);
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
throw new IllegalStateException("REA bridge requires an imported program");
|
||||
}
|
||||
sessionDefaultAddressSpace =
|
||||
currentProgram.getAddressFactory().getDefaultAddressSpace();
|
||||
if (currentProgram.getExecutableFormat().equals("Raw Binary") &&
|
||||
!currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
|
||||
.getBoolean("REA DOS COM prepared", false)) {
|
||||
throw new IllegalStateException("REA DOS COM entry/context preparation failed");
|
||||
}
|
||||
if (!Application.getApplicationVersion().equals(descriptor.providerVersion)) {
|
||||
throw new IllegalStateException("Ghidra provider version does not match the session");
|
||||
}
|
||||
@@ -171,7 +195,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
"Ghidra imported-byte digest does not match the admitted target"
|
||||
);
|
||||
}
|
||||
// GhidraScript wraps run() in a transaction. End it before serving so
|
||||
// each edit owns a real outer transaction and can roll back immediately.
|
||||
end(true);
|
||||
try {
|
||||
if (!descriptor.transport.equals("unix-socket") && descriptor.analysisExtensions.size() > 0)
|
||||
throw new IOException("Windows Ghidra P0 does not admit metadata recovery or database mutation.");
|
||||
analysisExtensions.analyze(descriptor.analysisExtensions, currentProgram, monitor);
|
||||
initializeDecompiler();
|
||||
serve(descriptor);
|
||||
}
|
||||
@@ -180,6 +210,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
decompiler.dispose();
|
||||
decompiler = null;
|
||||
}
|
||||
analysisExtensions.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,18 +250,23 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
JsonObject endpoint = new JsonObject();
|
||||
endpoint.addProperty("host", "127.0.0.1");
|
||||
endpoint.addProperty("port", address.getPort());
|
||||
// The Windows native reader refuses a file while a writer still
|
||||
// owns it. Closing CREATE_NEW publishes the complete record without
|
||||
// requesting rename rights on the retained private directory guard.
|
||||
Files.writeString(
|
||||
pendingPath,
|
||||
isWindowsHost() ? endpointPath : pendingPath,
|
||||
GSON.toJson(endpoint) + "\n",
|
||||
StandardCharsets.UTF_8,
|
||||
StandardOpenOption.CREATE_NEW,
|
||||
StandardOpenOption.WRITE
|
||||
);
|
||||
try {
|
||||
Files.move(pendingPath, endpointPath, StandardCopyOption.ATOMIC_MOVE);
|
||||
}
|
||||
catch (AtomicMoveNotSupportedException exception) {
|
||||
Files.move(pendingPath, endpointPath);
|
||||
if (!isWindowsHost()) {
|
||||
try {
|
||||
Files.move(pendingPath, endpointPath, StandardCopyOption.ATOMIC_MOVE);
|
||||
}
|
||||
catch (AtomicMoveNotSupportedException exception) {
|
||||
Files.move(pendingPath, endpointPath);
|
||||
}
|
||||
}
|
||||
acceptClient(server, descriptor);
|
||||
}
|
||||
@@ -240,6 +276,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWindowsHost() {
|
||||
return System.getProperty("os.name", "").startsWith("Windows");
|
||||
}
|
||||
|
||||
private void acceptClient(
|
||||
ServerSocketChannel server,
|
||||
SessionDescriptor descriptor) throws Exception {
|
||||
@@ -309,6 +349,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
);
|
||||
}
|
||||
return switch (request.method) {
|
||||
case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
|
||||
case "read_bytes" -> readMemoryBytes(request.params);
|
||||
case "address_to_file_offset" -> addressToFileOffset(request.params);
|
||||
case "address_name" -> addressName(request.params);
|
||||
case "list_documents" -> listDocuments(request.params);
|
||||
case "list_names" -> listNames(request.params);
|
||||
@@ -331,6 +374,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
|
||||
case "xrefs" -> xrefs(request.params);
|
||||
case "analyze_function" -> analyzeFunction(request.params);
|
||||
case "annotate_native_function" -> {
|
||||
if (!descriptor.transport.equals("unix-socket"))
|
||||
throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
|
||||
yield annotateNativeFunction(request.params);
|
||||
}
|
||||
default -> throw new RequestFailure(
|
||||
"method_unavailable",
|
||||
"Bridge method is unavailable"
|
||||
@@ -363,11 +411,217 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.addProperty("run_id", descriptor.runId);
|
||||
result.addProperty("profile_digest", descriptor.profileDigest);
|
||||
result.add("provider", provider);
|
||||
result.addProperty("read_only", true);
|
||||
boolean readOnly = !descriptor.transport.equals("unix-socket");
|
||||
result.addProperty("read_only", readOnly);
|
||||
result.addProperty("analysis_complete", !timedOut);
|
||||
result.addProperty("analysis_timed_out", timedOut);
|
||||
result.add("capabilities", GSON.toJsonTree(CAPABILITIES));
|
||||
result.add("capabilities", GSON.toJsonTree(
|
||||
java.util.Arrays.stream(CAPABILITIES)
|
||||
.filter(value -> !readOnly || !value.equals("annotate_native_function"))
|
||||
.toArray(String[]::new)
|
||||
));
|
||||
result.add("target", target);
|
||||
JsonArray extensions = analysisExtensions.reports();
|
||||
if (extensions.size() > 0) result.add("analysis_extensions", extensions);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String bytesHex(byte[] bytes) {
|
||||
return java.util.HexFormat.of().formatHex(bytes);
|
||||
}
|
||||
|
||||
private String hashFileBytes(FileBytes source, boolean modified) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] chunk = new byte[65536];
|
||||
for (long offset = 0; offset < source.getSize();) {
|
||||
monitor.checkCancelled();
|
||||
int length = (int) Math.min(chunk.length, source.getSize() - offset);
|
||||
int read = modified
|
||||
? source.getModifiedBytes(offset, chunk, 0, length)
|
||||
: source.getOriginalBytes(offset, chunk, 0, length);
|
||||
if (read != length) throw new IOException("Incomplete FileBytes read at " + offset);
|
||||
digest.update(chunk, 0, read);
|
||||
offset += read;
|
||||
}
|
||||
return bytesHex(digest.digest());
|
||||
}
|
||||
|
||||
private String hashMemory(Address start, long length) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] chunk = new byte[65536];
|
||||
for (long offset = 0; offset < length;) {
|
||||
monitor.checkCancelled();
|
||||
int count = (int) Math.min(chunk.length, length - offset);
|
||||
int read = currentProgram.getMemory().getBytes(start.add(offset), chunk, 0, count);
|
||||
if (read != count) throw new IOException("Incomplete memory read at " + start.add(offset));
|
||||
digest.update(chunk, 0, read);
|
||||
offset += read;
|
||||
}
|
||||
return bytesHex(digest.digest());
|
||||
}
|
||||
|
||||
private JsonObject inspectNativeLoadImage(JsonObject params) throws Exception {
|
||||
requireKeys(params, Set.of());
|
||||
JsonObject result = new JsonObject();
|
||||
JsonArray recovery = analysisExtensions.inspectImage();
|
||||
if (recovery.size() > 0) result.add("metadata_recovery", recovery);
|
||||
result.addProperty("executable_format", currentProgram.getExecutableFormat());
|
||||
result.addProperty("language_id", currentProgram.getLanguageID().getIdAsString());
|
||||
result.addProperty("compiler_spec_id", currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString());
|
||||
result.addProperty("image_base", canonicalAddress(currentProgram.getImageBase()));
|
||||
result.addProperty("default_address_space", sessionDefaultAddressSpace.getName());
|
||||
List<FileBytes> files = currentProgram.getMemory().getAllFileBytes();
|
||||
JsonArray sources = new JsonArray();
|
||||
for (FileBytes file : files) {
|
||||
JsonObject source = new JsonObject();
|
||||
source.addProperty("name", file.getFilename());
|
||||
source.addProperty("size", file.getSize());
|
||||
source.addProperty("original_sha256", hashFileBytes(file, false));
|
||||
source.addProperty("modified_sha256", hashFileBytes(file, true));
|
||||
sources.add(source);
|
||||
}
|
||||
result.add("source_files", sources);
|
||||
JsonArray mappings = new JsonArray();
|
||||
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
|
||||
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject mapping = new JsonObject();
|
||||
mapping.addProperty("block", block.getName());
|
||||
mapping.addProperty("start", canonicalAddress(source.getMinAddress()));
|
||||
mapping.addProperty("end", canonicalAddress(source.getMaxAddress()));
|
||||
mapping.addProperty("address_space", source.getMinAddress().getAddressSpace().getName());
|
||||
mapping.addProperty("initialized", block.isInitialized());
|
||||
mapping.addProperty("loaded", block.isLoaded());
|
||||
mapping.addProperty("overlay", block.isOverlay());
|
||||
mapping.addProperty("length", source.getLength());
|
||||
FileBytes backing = source.getFileBytes().orElse(null);
|
||||
int sourceIndex = backing == null ? -1 : files.indexOf(backing);
|
||||
if (sourceIndex < 0) mapping.add("source_file_index", JsonNull.INSTANCE);
|
||||
else mapping.addProperty("source_file_index", sourceIndex);
|
||||
long offset = source.getFileBytesOffset();
|
||||
if (offset < 0) mapping.add("file_offset", JsonNull.INSTANCE);
|
||||
else mapping.addProperty("file_offset", offset);
|
||||
if (block.isInitialized()) mapping.addProperty("sha256", hashMemory(source.getMinAddress(), source.getLength()));
|
||||
else mapping.add("sha256", JsonNull.INSTANCE);
|
||||
mappings.add(mapping);
|
||||
}
|
||||
}
|
||||
result.add("mappings", mappings);
|
||||
JsonArray relocations = new JsonArray();
|
||||
Iterator<Relocation> iterator = currentProgram.getRelocationTable().getRelocations();
|
||||
while (iterator.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
Relocation relocation = iterator.next();
|
||||
Address location = relocation.getAddress();
|
||||
JsonObject row = new JsonObject();
|
||||
row.addProperty("address", canonicalAddress(location));
|
||||
if (location instanceof SegmentedAddress segmented) {
|
||||
row.addProperty("segment", segmented.getSegment());
|
||||
row.addProperty("offset", segmented.getSegmentOffset());
|
||||
} else {
|
||||
row.add("segment", JsonNull.INSTANCE);
|
||||
row.add("offset", JsonNull.INSTANCE);
|
||||
}
|
||||
row.addProperty("status", relocation.getStatus().toString());
|
||||
row.addProperty("type", relocation.getType());
|
||||
row.add("values", GSON.toJsonTree(relocation.getValues() == null ? new long[0] : relocation.getValues()));
|
||||
byte[] original = relocation.getBytes();
|
||||
row.add("original_bytes_hex", original == null ? JsonNull.INSTANCE : GSON.toJsonTree(bytesHex(original)));
|
||||
if (original != null && original.length > 0) {
|
||||
byte[] memory = new byte[original.length];
|
||||
int read = currentProgram.getMemory().getBytes(location, memory);
|
||||
row.add("memory_bytes_hex", read == memory.length ? GSON.toJsonTree(bytesHex(memory)) : JsonNull.INSTANCE);
|
||||
} else row.add("memory_bytes_hex", JsonNull.INSTANCE);
|
||||
relocations.add(row);
|
||||
}
|
||||
result.add("relocations", relocations);
|
||||
List<Address> entries = new ArrayList<>();
|
||||
var entryIterator = currentProgram.getSymbolTable().getExternalEntryPointIterator();
|
||||
while (entryIterator.hasNext()) entries.add(entryIterator.next());
|
||||
entries.sort(Address::compareTo);
|
||||
JsonArray entryPoints = new JsonArray();
|
||||
for (Address entry : entries) entryPoints.add(canonicalAddress(entry));
|
||||
result.add("entry_points", entryPoints);
|
||||
JsonArray contexts = new JsonArray();
|
||||
for (Address entry : entries) {
|
||||
JsonObject context = new JsonObject();
|
||||
context.addProperty("address", canonicalAddress(entry));
|
||||
JsonArray registers = new JsonArray();
|
||||
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
||||
Register register = currentProgram.getRegister(name);
|
||||
if (register == null) continue;
|
||||
JsonObject row = new JsonObject();
|
||||
row.addProperty("name", name);
|
||||
BigInteger value = currentProgram.getProgramContext().getValue(register, entry, false);
|
||||
row.add("value_hex", value == null ? JsonNull.INSTANCE : GSON.toJsonTree("0x" + value.toString(16)));
|
||||
registers.add(row);
|
||||
}
|
||||
context.add("registers", registers);
|
||||
contexts.add(context);
|
||||
}
|
||||
result.add("entry_context", contexts);
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonObject readMemoryBytes(JsonObject params) throws Exception {
|
||||
requireKeys(params, Set.of("document", "address", "length"));
|
||||
requireDocument(params);
|
||||
Address start = requireAddress(params, "address");
|
||||
JsonElement requested = params.get("length");
|
||||
long length;
|
||||
try {
|
||||
if (requested == null || !requested.isJsonPrimitive() || !requested.getAsJsonPrimitive().isNumber())
|
||||
throw new IllegalArgumentException();
|
||||
length = requested.getAsBigDecimal().longValueExact();
|
||||
if (length < 1 || length > 9007199254740991L) throw new IllegalArgumentException();
|
||||
} catch (RuntimeException failure) {
|
||||
throw new RequestFailure("invalid_request", "Byte length must be a positive safe integer");
|
||||
}
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte[] chunk = new byte[65536];
|
||||
for (long offset = 0; offset < length;) {
|
||||
monitor.checkCancelled();
|
||||
Address at;
|
||||
try { at = start.addNoWrap(offset); }
|
||||
catch (Exception endOfSpace) { break; }
|
||||
MemoryBlock block = currentProgram.getMemory().getBlock(at);
|
||||
if (block == null || !block.isInitialized()) break;
|
||||
int count = (int) Math.min(chunk.length, Math.min(length - offset, block.getEnd().subtract(at) + 1));
|
||||
int read = currentProgram.getMemory().getBytes(at, chunk, 0, count);
|
||||
bytes.write(chunk, 0, read);
|
||||
if (read != count) break;
|
||||
offset += read;
|
||||
}
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", canonicalAddress(start));
|
||||
result.addProperty("requested_bytes", length);
|
||||
result.addProperty("returned_bytes", bytes.size());
|
||||
result.addProperty("bytes_hex", bytesHex(bytes.toByteArray()));
|
||||
result.addProperty("complete", bytes.size() == length);
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonObject addressToFileOffset(JsonObject params) {
|
||||
requireKeys(params, Set.of("document", "address"));
|
||||
requireDocument(params);
|
||||
Address address = requireAddress(params, "address");
|
||||
MemoryBlock block = currentProgram.getMemory().getBlock(address);
|
||||
if (block == null || !block.isInitialized())
|
||||
throw new RequestFailure("invalid_request", "Address has no initialized file-backed memory: " + canonicalAddress(address));
|
||||
Long fileOffset = null;
|
||||
int mappings = 0;
|
||||
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
|
||||
if (!source.contains(address) || source.getFileBytes().isEmpty()) continue;
|
||||
long offset = source.getFileBytesOffset(address);
|
||||
if (offset < 0) continue;
|
||||
fileOffset = offset;
|
||||
mappings++;
|
||||
}
|
||||
if (mappings != 1)
|
||||
throw new RequestFailure("invalid_request", "Address has " + mappings + " authoritative source mappings: " + canonicalAddress(address));
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", canonicalAddress(address));
|
||||
result.addProperty("file_offset", fileOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -471,8 +725,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (!direction.equals("incoming") && !direction.equals("outgoing")) {
|
||||
throw new RequestFailure("invalid_request", "direction must be incoming or outgoing");
|
||||
}
|
||||
InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
|
||||
List<Reference> references = collectReferences(scan.instructions, direction);
|
||||
List<Reference> references = collectReferences(function, direction);
|
||||
JsonArray edges = new JsonArray();
|
||||
for (Reference reference : references) {
|
||||
edges.add(referenceEdge(reference));
|
||||
@@ -483,7 +736,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.add("references", edges);
|
||||
result.addProperty("reference_kinds_available", true);
|
||||
JsonArray unresolvedCalls = new JsonArray();
|
||||
if (direction.equals("outgoing")) for (Instruction instruction : scan.instructions) {
|
||||
if (direction.equals("outgoing")) for (Instruction instruction : scanInstructions(function, Integer.MAX_VALUE).instructions) {
|
||||
if (!instruction.getFlowType().isCall()) continue;
|
||||
boolean resolved = false;
|
||||
for (Reference reference : instruction.getReferencesFrom()) if (reference.getReferenceType().isCall()) resolved = true;
|
||||
@@ -579,6 +832,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.addProperty("total_fields", totalFields);
|
||||
result.addProperty("truncated", totalFields > 20_000);
|
||||
result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Nested and recursive types retain category-path identities for separate inspection.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
|
||||
if (type != null) {
|
||||
JsonObject metadata = analysisExtensions.inspectDataType(currentProgram, type, address, monitor);
|
||||
if (metadata != null) result.add("metadata_recovery", metadata);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -609,6 +866,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
JsonArray operands = new JsonArray();
|
||||
JsonArray references = new JsonArray();
|
||||
JsonArray destinations = new JsonArray();
|
||||
JsonArray flowEvidence = new JsonArray();
|
||||
JsonObject flow = new JsonObject();
|
||||
flow.addProperty("kind", "unavailable");
|
||||
flow.addProperty("conditional", false);
|
||||
@@ -624,7 +882,20 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.addProperty("mnemonic", instruction.getMnemonicString());
|
||||
result.addProperty("raw_disassembly", instruction.toString());
|
||||
RefType type = instruction.getFlowType();
|
||||
flow.addProperty("kind", type.isCall() ? "call" : type.isJump() ? "jump" : type.isTerminal() ? "terminal" : "fallthrough");
|
||||
JsonObject listingFlow = new JsonObject();
|
||||
listingFlow.addProperty("source", "ghidra-listing-flow-type");
|
||||
listingFlow.addProperty("value", type.getName());
|
||||
flowEvidence.add(listingFlow);
|
||||
boolean decodedReturn = false;
|
||||
for (PcodeOp operation : instruction.getPcode()) {
|
||||
if (operation.getOpcode() != PcodeOp.RETURN) continue;
|
||||
decodedReturn = true;
|
||||
JsonObject returnEvidence = new JsonObject();
|
||||
returnEvidence.addProperty("source", "ghidra-instruction-pcode");
|
||||
returnEvidence.addProperty("value", operation.getMnemonic());
|
||||
flowEvidence.add(returnEvidence);
|
||||
}
|
||||
flow.addProperty("kind", type.isCall() ? "call" : type.isJump() ? "jump" : decodedReturn ? "return" : type.isTerminal() ? "terminal" : "fallthrough");
|
||||
flow.addProperty("conditional", type.isConditional());
|
||||
flow.addProperty("computed", type.isComputed());
|
||||
if (!type.isComputed()) for (Address target : instruction.getFlows()) destinations.add(canonicalAddress(target));
|
||||
@@ -660,10 +931,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
}
|
||||
flow.add("direct_destinations", destinations);
|
||||
flow.add("classification_evidence", flowEvidence);
|
||||
result.add("operands", operands);
|
||||
result.add("flow", flow);
|
||||
result.add("references", references);
|
||||
result.add("limitations", GSON.toJsonTree(List.of("Operand components are Ghidra Listing decoder tokens; effective memory base/index/displacement roles and per-instruction context mode are unavailable. The mode field is the program language variant.", "Terminal flow is not classified as a return without provider evidence.")));
|
||||
List<String> limitations = new ArrayList<>(List.of("Operand components are Ghidra Listing decoder tokens; effective memory base/index/displacement roles and per-instruction context mode are unavailable. The mode field is the program language variant."));
|
||||
if (flow.get("kind").getAsString().equals("terminal")) limitations.add("No RETURN operation was observed in the provider's instruction p-code; terminal classification remains generic.");
|
||||
result.add("limitations", GSON.toJsonTree(limitations));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -725,6 +999,126 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void invalidateAnalysisCaches() {
|
||||
nameInventory = null;
|
||||
procedureInventory = null;
|
||||
stringInventory = null;
|
||||
if (decompiler != null) decompiler.flushCache();
|
||||
}
|
||||
|
||||
private JsonObject annotationReadback(Function function) {
|
||||
Address entry = function.getEntryPoint();
|
||||
JsonObject value = new JsonObject();
|
||||
value.addProperty("address", canonicalAddress(entry));
|
||||
value.addProperty("name", procedureName(function));
|
||||
value.addProperty("comment", currentProgram.getListing().getComment(CommentType.PRE, entry));
|
||||
value.addProperty("inline_comment", currentProgram.getListing().getComment(CommentType.EOL, entry));
|
||||
return value;
|
||||
}
|
||||
|
||||
private JsonObject annotateNativeFunction(JsonObject params) throws Exception {
|
||||
if (!Set.of("procedure", "name", "comment", "inline_comment").containsAll(params.keySet()))
|
||||
throw new RequestFailure("invalid_request", "Unknown function annotation field");
|
||||
if (!params.has("name") && !params.has("comment") && !params.has("inline_comment"))
|
||||
throw new RequestFailure("invalid_request", "Supply at least one annotation change");
|
||||
Function function = resolveProcedure(requireString(params, "procedure"));
|
||||
Address entry = function.getEntryPoint();
|
||||
if (function.isExternal() || !currentProgram.getMemory().contains(entry))
|
||||
throw new RequestFailure("invalid_request", "Annotations require a local function entry: " + canonicalAddress(entry));
|
||||
for (String field : List.of("name", "comment", "inline_comment")) {
|
||||
if (params.has(field)) validateAnnotationText(requireText(params, field), field);
|
||||
}
|
||||
String leafName = params.has("name")
|
||||
? annotationLeafName(function, requireString(params, "name")) : null;
|
||||
int transaction = currentProgram.startTransaction("REA function annotations");
|
||||
boolean commit = false;
|
||||
try {
|
||||
// Validate edits through Ghidra's own setters, including name rules.
|
||||
// A later invalid name must roll back earlier comment writes.
|
||||
for (String field : List.of("comment", "inline_comment")) {
|
||||
if (params.has(field)) {
|
||||
String text = requireText(params, field);
|
||||
CommentType type = field.equals("comment") ? CommentType.PRE : CommentType.EOL;
|
||||
currentProgram.getListing().setComment(entry, type, text.isEmpty() ? null : text);
|
||||
}
|
||||
}
|
||||
if (params.has("name"))
|
||||
function.setName(leafName, SourceType.USER_DEFINED);
|
||||
monitor.checkCancelled();
|
||||
invalidateAnalysisCaches();
|
||||
JsonObject readback = annotationReadback(function);
|
||||
for (String field : List.of("name", "comment", "inline_comment")) {
|
||||
if (!params.has(field)) continue;
|
||||
String requested = requireText(params, field);
|
||||
JsonElement measured = readback.get(field);
|
||||
String expected = field.equals("name") ? leafName : requested.isEmpty() ? null : requested;
|
||||
String observed = field.equals("name") ? function.getName() :
|
||||
(measured.isJsonNull() ? null : measured.getAsString());
|
||||
if (!java.util.Objects.equals(expected, observed))
|
||||
throw new RequestFailure("annotation_readback_mismatch", "Annotation readback differs for " + field + " at " + canonicalAddress(entry));
|
||||
}
|
||||
JsonObject query = new JsonObject();
|
||||
query.addProperty("procedure", canonicalAddress(entry));
|
||||
JsonObject result = new JsonObject();
|
||||
result.add("annotations", readback);
|
||||
result.add("dossier", analyzeFunction(query));
|
||||
JsonObject effects = new JsonObject();
|
||||
effects.addProperty("scope", "session-analysis-database");
|
||||
effects.addProperty("source_bytes_modified", false);
|
||||
effects.addProperty("persists_after_close", false);
|
||||
result.add("effects", effects);
|
||||
monitor.checkCancelled();
|
||||
commit = true;
|
||||
return result;
|
||||
}
|
||||
catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
|
||||
throw new RequestFailure("invalid_function_name", "Invalid function name at " + canonicalAddress(entry) + ": " + safeMessage(exception));
|
||||
}
|
||||
finally {
|
||||
currentProgram.endTransaction(transaction, commit);
|
||||
// Rollback also invalidates inventory and decompiler views.
|
||||
invalidateAnalysisCaches();
|
||||
}
|
||||
}
|
||||
|
||||
private String annotationLeafName(Function function, String requested) {
|
||||
// Fully qualified readback must be usable as an idempotent rename input.
|
||||
if (requested.equals(procedureName(function))) return function.getName();
|
||||
String namespace = function.getParentNamespace().isGlobal()
|
||||
? "" : function.getParentNamespace().getName(true);
|
||||
String prefix = namespace + "::";
|
||||
if (!namespace.isEmpty() && requested.startsWith(prefix)) {
|
||||
String leaf = requested.substring(prefix.length());
|
||||
if (!leaf.isEmpty()) return leaf;
|
||||
throw new RequestFailure("invalid_function_name",
|
||||
"Function name has an empty leaf within namespace " + namespace + " at " +
|
||||
canonicalAddress(function.getEntryPoint()) + ": " + requested);
|
||||
}
|
||||
return requested;
|
||||
}
|
||||
|
||||
private static String requireText(JsonObject params, String field) {
|
||||
JsonElement value = params.get(field);
|
||||
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
|
||||
throw new RequestFailure("invalid_request", "Annotation " + field + " must be text");
|
||||
return value.getAsString();
|
||||
}
|
||||
|
||||
private static void validateAnnotationText(String text, String field) {
|
||||
for (int index = 0; index < text.length(); index++) {
|
||||
char character = text.charAt(index);
|
||||
if (character == 0)
|
||||
throw new RequestFailure("invalid_request", "Annotation " + field + " contains NUL at UTF-16 index " + index + "; Ghidra's decompiler cannot marshal NUL text");
|
||||
if (Character.isHighSurrogate(character) && index + 1 < text.length() &&
|
||||
Character.isLowSurrogate(text.charAt(index + 1))) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (Character.isSurrogate(character))
|
||||
throw new RequestFailure("invalid_request", "Annotation " + field + " contains an unpaired Unicode surrogate at UTF-16 index " + index + "; Ghidra cannot preserve it as annotation text");
|
||||
}
|
||||
}
|
||||
|
||||
private JsonObject analyzeFunction(JsonObject params) throws Exception {
|
||||
requireKeys(params, Set.of("procedure"));
|
||||
Function function = resolveProcedure(requireString(params, "procedure"));
|
||||
@@ -734,11 +1128,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (pseudocode == null) {
|
||||
pseudocode = "";
|
||||
}
|
||||
List<Reference> incomingReferences = collectReferences(scan.instructions, "incoming")
|
||||
List<Reference> incomingReferences = collectReferences(function, "incoming")
|
||||
.stream()
|
||||
.filter(reference -> !function.getBody().contains(reference.getFromAddress()))
|
||||
.toList();
|
||||
List<Reference> outgoingReferences = collectReferences(scan.instructions, "outgoing");
|
||||
List<Reference> outgoingReferences = collectReferences(function, "outgoing");
|
||||
JsonArray incoming = referenceEdges(incomingReferences);
|
||||
JsonArray outgoing = referenceEdges(outgoingReferences);
|
||||
JsonArray comments = comments(scan.instructions);
|
||||
@@ -1131,6 +1525,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
: regexMatcher(expression, caseSensitive);
|
||||
JsonArray items = new JsonArray();
|
||||
for (InventoryItem item : inventory) {
|
||||
monitor.checkCancelled();
|
||||
if (!matcher.matches(item.value)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1241,33 +1636,47 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
|
||||
private Function resolveProcedure(String value) throws Exception {
|
||||
Address address = tryParseAddress(value);
|
||||
if (address != null) {
|
||||
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
|
||||
if (function == null && address.isMemoryAddress()) {
|
||||
function = currentProgram.getFunctionManager().getFunctionContaining(address);
|
||||
// Explicit address syntax wins; an unprefixed identifier is first an
|
||||
// exact symbol name, even when every character is a hexadecimal digit.
|
||||
boolean explicitAddress = isExplicitReaAddress(value);
|
||||
if (!explicitAddress) {
|
||||
List<Function> matches = new ArrayList<>();
|
||||
for (FunctionEntry entry : procedures()) {
|
||||
monitor.checkCancelled();
|
||||
Function function = entry.function;
|
||||
Symbol symbol = function.getSymbol();
|
||||
String qualified = symbol == null ? function.getName() : symbol.getName(true);
|
||||
boolean matchesName = function.getName().equals(value) || qualified.equals(value);
|
||||
if (!matchesName) {
|
||||
// Imported labels (for example Mach-O _main) can share a
|
||||
// function entry without becoming its primary symbol. Only
|
||||
// exact entries qualify; an interior label is not a selector.
|
||||
for (Symbol entrySymbol : currentProgram.getSymbolTable().getSymbols(function.getEntryPoint())) {
|
||||
monitor.checkCancelled();
|
||||
if (entrySymbol.getName().equals(value) || entrySymbol.getName(true).equals(value)) {
|
||||
matchesName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matchesName) matches.add(function);
|
||||
}
|
||||
if (function == null) {
|
||||
throw new RequestFailure("not_found", "No procedure exists at the requested address");
|
||||
}
|
||||
return function;
|
||||
if (matches.size() > 1)
|
||||
throw new RequestFailure("ambiguous", "Ghidra procedure name is ambiguous: " + value +
|
||||
"; select an exact entry address from " + matches.stream()
|
||||
.map(function -> canonicalAddress(function.getEntryPoint())).toList());
|
||||
if (matches.size() == 1) return matches.get(0);
|
||||
}
|
||||
List<Function> matches = new ArrayList<>();
|
||||
for (FunctionEntry entry : procedures()) {
|
||||
Function function = entry.function;
|
||||
Symbol symbol = function.getSymbol();
|
||||
String qualified = symbol == null ? function.getName() : symbol.getName(true);
|
||||
if (function.getName().equals(value) || qualified.equals(value)) {
|
||||
matches.add(function);
|
||||
}
|
||||
}
|
||||
if (matches.isEmpty()) {
|
||||
throw new RequestFailure("not_found", "Unknown Ghidra procedure name");
|
||||
}
|
||||
if (matches.size() != 1) {
|
||||
throw new RequestFailure("ambiguous", "Ghidra procedure name is ambiguous");
|
||||
}
|
||||
return matches.get(0);
|
||||
// Retain legacy bare-hex address input only when no symbol matches.
|
||||
Address address = explicitAddress ? parseReaAddress(value) : tryParseAddress(value);
|
||||
if (address == null)
|
||||
throw new RequestFailure("not_found", "Unknown Ghidra procedure name or address: " + value);
|
||||
Function function = currentProgram.getFunctionManager().getFunctionAt(address);
|
||||
if (function == null && address.isMemoryAddress())
|
||||
function = currentProgram.getFunctionManager().getFunctionContaining(address);
|
||||
if (function == null)
|
||||
throw new RequestFailure("not_found", "No procedure exists at the requested address: " + value);
|
||||
return function;
|
||||
}
|
||||
|
||||
private JsonObject nativeApiBoundary(
|
||||
@@ -2190,21 +2599,32 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
|
||||
private List<Reference> collectReferences(
|
||||
List<Instruction> instructions,
|
||||
Function function,
|
||||
String direction) throws Exception {
|
||||
// References can target instruction interiors or originate in embedded
|
||||
// data. Use actual body ownership, including an external's exact entry.
|
||||
AddressSet scope = new AddressSet(function.getBody());
|
||||
scope.add(function.getEntryPoint());
|
||||
boolean outgoing = direction.equals("outgoing");
|
||||
AddressIterator addresses = outgoing
|
||||
? currentProgram.getReferenceManager().getReferenceSourceIterator(scope, true)
|
||||
: currentProgram.getReferenceManager().getReferenceDestinationIterator(scope, true);
|
||||
TreeMap<String, Reference> observed = new TreeMap<>();
|
||||
for (Instruction instruction : instructions) {
|
||||
while (addresses.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
if (direction.equals("outgoing")) {
|
||||
Address address = addresses.next();
|
||||
if (outgoing) {
|
||||
for (Reference reference : currentProgram.getReferenceManager()
|
||||
.getReferencesFrom(instruction.getAddress())) {
|
||||
.getReferencesFrom(address)) {
|
||||
monitor.checkCancelled();
|
||||
addReference(observed, reference);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ReferenceIterator iterator = currentProgram.getReferenceManager()
|
||||
.getReferencesTo(instruction.getAddress());
|
||||
.getReferencesTo(address);
|
||||
while (iterator.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
addReference(observed, iterator.next());
|
||||
}
|
||||
}
|
||||
@@ -2604,10 +3024,46 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return parseReaAddress(requireString(params, name));
|
||||
}
|
||||
|
||||
private static boolean isExplicitReaAddress(String value) {
|
||||
if (value.regionMatches(true, 0, "0x", 0, 2) &&
|
||||
isHexadecimal(value, 2, value.length()))
|
||||
return true;
|
||||
int separator = value.toLowerCase(Locale.ROOT).lastIndexOf(":0x");
|
||||
if (separator <= 0 || !isHexadecimal(value, separator + 3, value.length()))
|
||||
return false;
|
||||
// Classify the encoded space iteratively. A repeated regex alternation
|
||||
// can exhaust Java's stack on a long ordinary symbol name.
|
||||
for (int index = 0; index < separator; index++) {
|
||||
char item = value.charAt(index);
|
||||
if ((item >= 'A' && item <= 'Z') ||
|
||||
(item >= 'a' && item <= 'z') ||
|
||||
(item >= '0' && item <= '9') ||
|
||||
item == '.' || item == '_' || item == '~' || item == '-')
|
||||
continue;
|
||||
if (item != '%' || index + 2 >= separator ||
|
||||
!isHexadecimal(value, index + 1, index + 3))
|
||||
return false;
|
||||
index += 2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isHexadecimal(String value, int start, int end) {
|
||||
if (start >= end) return false;
|
||||
for (int index = start; index < end; index++) {
|
||||
char item = value.charAt(index);
|
||||
if (!((item >= '0' && item <= '9') ||
|
||||
(item >= 'a' && item <= 'f') ||
|
||||
(item >= 'A' && item <= 'F')))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Address parseReaAddress(String value) {
|
||||
Address address = tryParseAddress(value);
|
||||
if (address == null) {
|
||||
throw new RequestFailure("invalid_request", "Unknown or invalid Ghidra address");
|
||||
throw new RequestFailure("invalid_request", "Unknown or invalid Ghidra address: " + value);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
@@ -2616,22 +3072,31 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
try {
|
||||
String spaceName = null;
|
||||
String offset = value;
|
||||
int separator = value.lastIndexOf(":0x");
|
||||
int separator = value.toLowerCase(Locale.ROOT).lastIndexOf(":0x");
|
||||
if (separator >= 0) {
|
||||
spaceName = decodeAddressSpace(value.substring(0, separator));
|
||||
offset = value.substring(separator + 3);
|
||||
}
|
||||
else if (value.startsWith("0x")) {
|
||||
else if (value.startsWith("0x") || value.startsWith("0X")) {
|
||||
offset = value.substring(2);
|
||||
}
|
||||
if (!offset.matches("[0-9A-Fa-f]+")) {
|
||||
if (!isHexadecimal(offset, 0, offset.length())) {
|
||||
return null;
|
||||
}
|
||||
AddressSpace space = spaceName == null
|
||||
? currentProgram.getAddressFactory().getDefaultAddressSpace()
|
||||
: currentProgram.getAddressFactory().getAddressSpace(spaceName);
|
||||
return space == null ? null : space.getAddress(offset);
|
||||
if (space == null) return null;
|
||||
Address address = space.getAddress(offset);
|
||||
BigInteger requestedOffset = new BigInteger(offset, 16);
|
||||
BigInteger parsedOffset = new BigInteger(Long.toUnsignedString(address.getOffset()));
|
||||
if (!requestedOffset.equals(parsedOffset))
|
||||
throw new RequestFailure("invalid_request",
|
||||
"Ghidra address offset cannot be represented without truncation in space " +
|
||||
space.getName() + ": requested " + value + ", parsed " + canonicalAddress(address));
|
||||
return address;
|
||||
}
|
||||
catch (RequestFailure failure) { throw failure; }
|
||||
catch (Exception exception) {
|
||||
return null;
|
||||
}
|
||||
@@ -2715,19 +3180,40 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
);
|
||||
}
|
||||
catch (PatternSyntaxException exception) {
|
||||
throw new RequestFailure("invalid_request", "Invalid regex pattern");
|
||||
// Java wraps compiler stack exhaustion in PatternSyntaxException
|
||||
// without retaining its cause. This producer reason is not bad syntax.
|
||||
if (exception.getDescription().equals("Stack overflow during pattern compilation"))
|
||||
throw regexStackExhausted(expression, "compiling");
|
||||
throw new RequestFailure("invalid_request", "Invalid regex pattern " + expression + ": " + exception.getDescription() + " at index " + exception.getIndex());
|
||||
}
|
||||
return value -> pattern.matcher(value).find();
|
||||
catch (StackOverflowError exhausted) {
|
||||
throw regexStackExhausted(expression, "compiling");
|
||||
}
|
||||
return value -> {
|
||||
try {
|
||||
return pattern.matcher(value).find();
|
||||
}
|
||||
catch (StackOverflowError exhausted) {
|
||||
throw regexStackExhausted(expression, "matching");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static RequestFailure regexStackExhausted(String expression, String stage) {
|
||||
return new RequestFailure("regex_stack_exhausted",
|
||||
"Ghidra's Java regex engine exhausted its stack while " + stage +
|
||||
" pattern " + expression + ". Use literal mode or simplify the regex; the session remains usable.");
|
||||
}
|
||||
|
||||
private static SessionDescriptor readDescriptor(Path path) throws IOException {
|
||||
if (!Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {
|
||||
throw new IllegalArgumentException("REA session descriptor is invalid");
|
||||
}
|
||||
JsonObject object = requireObject(
|
||||
JsonParser.parseString(Files.readString(path, StandardCharsets.UTF_8)),
|
||||
DESCRIPTOR_KEYS
|
||||
);
|
||||
JsonElement parsed = JsonParser.parseString(Files.readString(path, StandardCharsets.UTF_8));
|
||||
Set<String> keys = new HashSet<>(DESCRIPTOR_KEYS);
|
||||
if (parsed.isJsonObject() && parsed.getAsJsonObject().has("analysis_extensions"))
|
||||
keys.add("analysis_extensions");
|
||||
JsonObject object = requireObject(parsed, keys);
|
||||
return new SessionDescriptor(
|
||||
requireString(object, "transport"),
|
||||
requireString(object, "endpoint_path"),
|
||||
@@ -2735,7 +3221,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
requireString(object, "run_id"),
|
||||
requireSha256(object, "target_sha256"),
|
||||
requireString(object, "provider_version"),
|
||||
requireString(object, "profile_digest")
|
||||
requireString(object, "profile_digest"),
|
||||
object.has("analysis_extensions") ? object.getAsJsonArray("analysis_extensions") : new JsonArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2772,7 +3259,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
private void requireDocument(JsonObject params) {
|
||||
String document = optionalString(params, "document");
|
||||
if (document != null && !document.equals(currentProgram.getName())) {
|
||||
throw new RequestFailure("not_found", "Unknown Ghidra Program identity");
|
||||
throw new RequestFailure("not_found", "Unknown Ghidra Program identity: " + document + "; active program: " + currentProgram.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2884,7 +3371,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
|
||||
private static void writeResponse(BufferedWriter writer, JsonObject response) throws IOException {
|
||||
String encoded = GSON.toJson(response);
|
||||
writer.write(encoded);
|
||||
// Gson leaves UTF-16 surrogates literal. Escape them in JSON so even
|
||||
// diagnostics quoting ill-formed caller text remain lossless UTF-8.
|
||||
int start = 0;
|
||||
for (int index = 0; index < encoded.length(); index++) {
|
||||
if (!Character.isSurrogate(encoded.charAt(index))) continue;
|
||||
writer.write(encoded, start, index - start);
|
||||
writer.write("\\u" + Integer.toHexString(encoded.charAt(index)));
|
||||
start = index + 1;
|
||||
}
|
||||
writer.write(encoded, start, encoded.length() - start);
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
}
|
||||
@@ -2919,7 +3415,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
String runId,
|
||||
String targetSha256,
|
||||
String providerVersion,
|
||||
String profileDigest
|
||||
String profileDigest,
|
||||
JsonArray analysisExtensions
|
||||
) {}
|
||||
private record Request(int id, String method, JsonObject params) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Session-owned extension loading; analyzer algorithms stay behind each adapter ABI. */
|
||||
final class ReaGhidraExtensions implements AutoCloseable {
|
||||
private record Loaded(URLClassLoader loader, Object instance, String sha256) {}
|
||||
private final List<Loaded> loaded = new ArrayList<>();
|
||||
private final JsonArray reports = new JsonArray();
|
||||
|
||||
void analyze(JsonArray descriptors, Program program, TaskMonitor monitor) throws Exception {
|
||||
for (var item : descriptors) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject descriptor = item.getAsJsonObject();
|
||||
String id = descriptor.get("id").getAsString();
|
||||
String entry = descriptor.get("entry_class").getAsString();
|
||||
String digest = descriptor.get("sha256").getAsString();
|
||||
JsonObject report = new JsonObject();
|
||||
report.addProperty("id", id);
|
||||
report.addProperty("sha256", digest);
|
||||
URLClassLoader loader = null;
|
||||
try {
|
||||
if (!id.equals("nativeaot") || !entry.equals("rea.extensions.nativeaot.NativeAotExtension") ||
|
||||
descriptor.get("integration_api").getAsInt() != 1)
|
||||
throw new IllegalArgumentException("Unregistered Ghidra extension ABI: " + id);
|
||||
Path path = Path.of(descriptor.get("path").getAsString());
|
||||
if (!path.isAbsolute() || !Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS) || Files.size(path) > 8 * 1024 * 1024)
|
||||
throw new IllegalArgumentException("Invalid private extension artifact: " + path);
|
||||
String measured = java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(path)));
|
||||
if (!measured.equals(digest)) throw new IllegalArgumentException("Private extension digest mismatch: " + id);
|
||||
loader = new URLClassLoader(new java.net.URL[] { path.toUri().toURL() }, getClass().getClassLoader());
|
||||
Class<?> type = Class.forName(entry, true, loader);
|
||||
Object instance = type.getConstructor().newInstance();
|
||||
Object response = type.getMethod("analyze", Program.class, TaskMonitor.class).invoke(instance, program, monitor);
|
||||
if (!(response instanceof JsonObject result)) throw new IllegalArgumentException("Invalid Ghidra extension result: " + id);
|
||||
report.addProperty("status", result.get("status").getAsString());
|
||||
report.add("reason", result.get("reason").deepCopy());
|
||||
report.add("result", result);
|
||||
loaded.add(new Loaded(loader, instance, digest));
|
||||
loader = null;
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
failure(report, ex.getCause());
|
||||
} catch (Exception | LinkageError ex) {
|
||||
failure(report, ex);
|
||||
} finally {
|
||||
if (loader != null) loader.close();
|
||||
}
|
||||
reports.add(report);
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray reports() { return reports.deepCopy(); }
|
||||
|
||||
JsonArray inspectImage() throws Exception {
|
||||
JsonArray summaries = new JsonArray();
|
||||
for (Loaded extension : loaded) {
|
||||
Object response = extension.instance.getClass().getMethod("inspectImage").invoke(extension.instance);
|
||||
if (!(response instanceof JsonObject summary)) throw new IllegalArgumentException("Invalid extension image summary");
|
||||
summary.addProperty("analysis_artifact_sha256", extension.sha256);
|
||||
summaries.add(summary);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
JsonObject inspectDataType(Program program, DataType type, Address address, TaskMonitor monitor) throws Exception {
|
||||
for (Loaded extension : loaded) {
|
||||
try {
|
||||
Object response = extension.instance.getClass().getMethod("inspectDataType", Program.class, DataType.class, Address.class, TaskMonitor.class)
|
||||
.invoke(extension.instance, program, type, address, monitor);
|
||||
if (response instanceof JsonObject result) return result;
|
||||
if (response != null) throw new IllegalArgumentException("Invalid extension type metadata result");
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
throw new IllegalStateException("Ghidra extension type inspection failed: " + ex.getCause(), ex.getCause());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void failure(JsonObject report, Throwable ex) {
|
||||
String reason = "Extension loading/analysis failed: " + ex;
|
||||
if (ex.getCause() != null) reason += "; caused by " + ex.getCause();
|
||||
report.addProperty("status", "failed");
|
||||
report.addProperty("reason", reason);
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("loader_failure", reason);
|
||||
report.add("result", result);
|
||||
}
|
||||
|
||||
@Override public void close() throws Exception {
|
||||
Exception failure = null;
|
||||
for (Loaded extension : loaded) {
|
||||
try { extension.loader.close(); }
|
||||
catch (Exception ex) { if (failure == null) failure = ex; else failure.addSuppressed(ex); }
|
||||
}
|
||||
loaded.clear();
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/** Seed the declared DOS COM analysis context before default auto-analysis. */
|
||||
public final class ReaGhidraPrepareCom extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (!currentProgram.getLanguageID().getIdAsString().equals("x86:LE:16:Real Mode") ||
|
||||
!currentProgram.getExecutableFormat().equals("Raw Binary")) {
|
||||
throw new IllegalArgumentException("DOS COM preparation requires raw x86 real-mode import");
|
||||
}
|
||||
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress("1000:0100");
|
||||
Address start = currentProgram.getMinAddress();
|
||||
Address end = currentProgram.getMaxAddress();
|
||||
if (!entry.equals(start) || end.subtract(start) + 1 > 0xff00) {
|
||||
throw new IllegalArgumentException("DOS COM import must map 1..65280 bytes at 1000:0100");
|
||||
}
|
||||
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
||||
monitor.checkCancelled();
|
||||
currentProgram.getProgramContext().setValue(
|
||||
currentProgram.getRegister(name), start, end, BigInteger.valueOf(0x1000)
|
||||
);
|
||||
}
|
||||
addEntryPoint(entry);
|
||||
if (!disassemble(entry) || createFunction(entry, "entry") == null) {
|
||||
throw new IllegalStateException("DOS COM entry cannot be decoded as a function");
|
||||
}
|
||||
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA DOS COM prepared", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.util.LinkedHashSet;
|
||||
import nativeaot.rtr.ReadyToRunDirectory;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import nativeaot.rtr.SignatureReadyToRunLocator;
|
||||
import nativeaot.rtr.SymbolReadyToRunLocator;
|
||||
|
||||
/** Validate the producer's NativeAOT row layout before database mutation. */
|
||||
final class NativeAotDirectory {
|
||||
record Located(Address address, ReadyToRunDirectory directory, String discovery) {}
|
||||
|
||||
static Located locate(Program program, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
Address[] symbols = SymbolReadyToRunLocator.instance.locateModules(program, monitor, log);
|
||||
String discovery = symbols.length == 0 ? "signature-heuristic" : "symbol";
|
||||
Address[] found = symbols.length == 0
|
||||
? SignatureReadyToRunLocator.instance.locateModules(program, monitor, log) : symbols;
|
||||
var candidates = new LinkedHashSet<Address>();
|
||||
for (Address address : found) if (address.getOffset() != 0) candidates.add(address);
|
||||
if (candidates.isEmpty()) return null;
|
||||
if (candidates.size() != 1)
|
||||
throw new UnsupportedOperationException("Ambiguous NativeAOT directory candidates: " + candidates);
|
||||
Address address = candidates.iterator().next();
|
||||
var memory = program.getMemory();
|
||||
byte[] header = new byte[16];
|
||||
if (memory.getBytes(address, header) != header.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT header at " + address);
|
||||
int count = Short.toUnsignedInt(memory.getShort(address.add(12)));
|
||||
if (count < 1 || count > 80 || Byte.toUnsignedInt(header[14]) != 24 || header[15] != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT row layout at " + address +
|
||||
": sections=" + count + ", entry_size=" + Byte.toUnsignedInt(header[14]) + ", entry_type=" + Byte.toUnsignedInt(header[15]));
|
||||
byte[] rows = new byte[count * 24];
|
||||
if (memory.getBytes(address.add(16), rows) != rows.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT directory rows at " + address);
|
||||
ReadyToRunDirectory directory = ReadyToRunDirectory.readAtAddress(program, address);
|
||||
if (directory.getMajorVersion() != 9 || directory.getMinorVersion() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT RTR format " +
|
||||
directory.getMajorVersion() + "." + directory.getMinorVersion() + " at " + address + "; verified format is 9.1 (.NET 8.0.22 Linux x64 fixture).");
|
||||
var types = new LinkedHashSet<Integer>();
|
||||
for (ReadyToRunSection section : directory.getSections()) {
|
||||
monitor.checkCancelled();
|
||||
if (!types.add(section.getType())) throw new IllegalArgumentException("Duplicate NativeAOT section " + section.getType() + " at " + address);
|
||||
// ModuleInfoRow flags bit 0 declares an end pointer. ThreadStaticIndex
|
||||
// (204) is a single-address entry with flags=0 and an absent end.
|
||||
if (section.getFlags() != 0 && section.getFlags() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT section flags " + section.getFlags() + " for section " + section.getType());
|
||||
boolean hasEnd = section.getFlags() == 1;
|
||||
if (!hasEnd && section.getEnd() != 0)
|
||||
throw new IllegalArgumentException("NativeAOT single-address section " + section.getType() + " reports an unexpected end");
|
||||
if (hasEnd && Long.compareUnsigned(section.getStart(), section.getEnd()) > 0)
|
||||
throw new IllegalArgumentException("Reversed NativeAOT section " + section.getType() + " at " + address);
|
||||
if (section.getStart() != 0 && (!hasEnd || section.getStart() != section.getEnd()) &&
|
||||
(!memory.contains(address.getNewAddress(section.getStart())) ||
|
||||
(hasEnd && !memory.contains(address.getNewAddress(section.getEnd() - 1)))))
|
||||
throw new IllegalArgumentException("Unmapped NativeAOT section " + section.getType() + " at " + address);
|
||||
}
|
||||
if (directory.getSectionByType(ReadyToRunSection.DEHYDRATED_DATA) == null)
|
||||
throw new UnsupportedOperationException("NativeAOT directory at " + address + " has no DEHYDRATED_DATA; non-dehydrated layouts are not verified.");
|
||||
for (int type : new int[] { ReadyToRunSection.DEHYDRATED_DATA, ReadyToRunSection.FROZEN_OBJECT_REGION }) {
|
||||
ReadyToRunSection section = directory.getSectionByType(type);
|
||||
if (section != null && section.getFlags() != 1)
|
||||
throw new IllegalArgumentException("NativeAOT section " + type + " requires an end pointer");
|
||||
}
|
||||
return new Located(address, directory, discovery);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.FrozenObjectAnnotator;
|
||||
import nativeaot.objectmodel.MethodTableCrawler;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import nativeaot.rehydration.MetadataRehydratorNet80;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** Optional headless facade over the unchanged upstream analysis primitives. */
|
||||
public final class NativeAotExtension {
|
||||
private MethodTableManager manager;
|
||||
private NativeAotDirectory.Located located;
|
||||
private JsonObject report;
|
||||
private final List<String> diagnostics = new ArrayList<>();
|
||||
private JsonObject derivedRange;
|
||||
|
||||
public JsonObject analyze(Program program, TaskMonitor monitor) throws Exception {
|
||||
report = new JsonObject();
|
||||
report.addProperty("id", "nativeaot");
|
||||
report.addProperty("integration_api", 1);
|
||||
report.addProperty("source_revision", "effeb734fc570c32650f88b159608979dc7b423e");
|
||||
report.addProperty("source_revision_authority", "build-reported-unattested");
|
||||
report.addProperty("status", "not_applicable");
|
||||
report.addProperty("reason", "No NativeAOT directory with the supported row layout was found.");
|
||||
report.addProperty("method_tables", 0);
|
||||
if (!program.getLanguageID().getIdAsString().startsWith("x86:LE:64"))
|
||||
return failure("unsupported", "NativeAOT adapter supports x86-64 only.");
|
||||
MessageLog log = new MessageLog();
|
||||
int transaction = program.startTransaction("REA NativeAOT metadata recovery");
|
||||
boolean commit = false;
|
||||
String stage = "directory-discovery";
|
||||
try {
|
||||
located = NativeAotDirectory.locate(program, monitor, log);
|
||||
if (located == null) return finish(log);
|
||||
report.addProperty("header_address", address(located.address()));
|
||||
report.addProperty("discovery", located.discovery());
|
||||
report.addProperty("format_major", located.directory().getMajorVersion());
|
||||
report.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
stage = "metadata-rehydration";
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.DEHYDRATED_DATA);
|
||||
Address start = located.address().getNewAddress(section.getStart());
|
||||
Address end = located.address().getNewAddress(section.getEnd());
|
||||
if (start.compareTo(end) >= 0) throw new IllegalArgumentException("Empty dehydrated section at " + start);
|
||||
var scan = new MetadataRehydratorNet80().rehydrate(program, new AddressSet(start, end).getFirstRange(), monitor, log);
|
||||
// Upstream reports an inclusive end at base + written length.
|
||||
long writtenLength = scan.getScanningRange().getLength() - 1;
|
||||
derivedRange = new JsonObject();
|
||||
derivedRange.addProperty("address", address(scan.getScanningRange().getMinAddress()));
|
||||
derivedRange.addProperty("size_bytes", writtenLength);
|
||||
derivedRange.addProperty("sha256", hashMemory(program, scan.getScanningRange().getMinAddress(), writtenLength, monitor));
|
||||
derivedRange.add("file_offset", JsonNull.INSTANCE);
|
||||
stage = "method-table-recovery";
|
||||
manager = MethodTableManager.createForDirectory(program, located.directory());
|
||||
var conventions = NativeAotMethodConventions.capture(program, monitor);
|
||||
new MethodTableCrawler(manager, program, scan).analyze(monitor, log);
|
||||
stage = "calling-convention-preservation";
|
||||
NativeAotMethodConventions.restore(program, manager, conventions, monitor);
|
||||
log.appendMsg("REA", "Preserved pre-recovery calling conventions; new functions use the loaded compiler specification default. Original method signatures remain unknown.");
|
||||
if (manager.getObjectMT() == null || manager.getMethodTableCount() == 0)
|
||||
throw new UnsupportedOperationException("No unambiguous System.Object method table was recovered at header " + located.address());
|
||||
stage = "frozen-object-annotation";
|
||||
if (manager.getStringMT() == null || scan.getPointerLocations().length == 0) {
|
||||
diagnostics.add("Frozen-object annotation unavailable: System.String table or pointer locations were not recovered.");
|
||||
} else {
|
||||
new FrozenObjectAnnotator(program, manager).analyze(located.directory(), scan.getPointerLocations(), monitor, log);
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
report.addProperty("method_tables", manager.getMethodTableCount());
|
||||
JsonObject coverage = auditFrozenObjects(program, scan.getPointerLocations(), monitor);
|
||||
report.add("coverage", coverage);
|
||||
if (coverage.get("frozen_objects_annotated").getAsInt() != coverage.get("frozen_object_candidates").getAsInt())
|
||||
diagnostics.add("Some frozen-object candidates lack committed instance annotations; inspect coverage counts and Ghidra diagnostics.");
|
||||
report.addProperty("status", diagnostics.isEmpty() ? "complete" : "partial");
|
||||
report.add("reason", JsonNull.INSTANCE);
|
||||
report.add("derived_memory", derivedRange.deepCopy());
|
||||
JsonArray tables = new JsonArray();
|
||||
for (var mt : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject identity = new JsonObject();
|
||||
identity.addProperty("address", address(mt.getAddress()));
|
||||
identity.addProperty("type", mt.getMTType().getPathName());
|
||||
tables.add(identity);
|
||||
}
|
||||
report.add("types", tables);
|
||||
commit = true;
|
||||
return finish(log);
|
||||
} catch (CancelledException ex) {
|
||||
manager = null;
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
manager = null;
|
||||
diagnostics.add(stage + ": " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
|
||||
report.addProperty("status", ex instanceof UnsupportedOperationException ? "unsupported" : "failed");
|
||||
report.addProperty("reason", stage + ": " + ex.getMessage());
|
||||
return finish(log);
|
||||
} finally {
|
||||
program.endTransaction(transaction, commit);
|
||||
}
|
||||
}
|
||||
|
||||
/** Inline discovery for the existing loaded-image inspection. */
|
||||
public JsonObject inspectImage() {
|
||||
JsonObject summary = new JsonObject();
|
||||
summary.addProperty("format", "dotnet-nativeaot");
|
||||
for (String key : new String[] { "status", "reason", "header_address", "discovery", "format_major", "format_minor", "method_tables", "derived_memory", "coverage", "diagnostics" })
|
||||
summary.add(key, report.has(key) ? report.get(key).deepCopy() : JsonNull.INSTANCE);
|
||||
summary.add("types", report.has("types") ? report.get("types").deepCopy() : new JsonArray());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Recovery covers the reported metadata candidates; it does not reconstruct original C# source, custom field layouts, every runtime object, or execution behavior.");
|
||||
limitations.add("Method prototypes and parameter names remain Ghidra inferences; verify them against instructions and call sites. Pre-recovery conventions are retained rather than universally forcing __thiscall.");
|
||||
limitations.add("NativeAOT type identities and relationships are inferred; generated names are not original names. Derived memory has no original file offsets.");
|
||||
summary.add("limitations", limitations);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/** Read recovered relationships without restoring or mutating database state. */
|
||||
public JsonObject inspectDataType(Program program, DataType type, Address selected, TaskMonitor monitor) throws Exception {
|
||||
if (manager == null) return null;
|
||||
return NativeAotTypeMetadata.describe(program, manager, located, type, report, derivedRange, monitor);
|
||||
}
|
||||
|
||||
private JsonObject auditFrozenObjects(Program program, Address[] pointerLocations, TaskMonitor monitor) throws Exception {
|
||||
JsonObject coverage = new JsonObject();
|
||||
int candidates = 0, annotated = 0;
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.FROZEN_OBJECT_REGION);
|
||||
if (section != null) for (Address location : section.getPointersInSection(pointerLocations)) {
|
||||
monitor.checkCancelled();
|
||||
var table = manager.getMethodTable(program.getMemory().getLong(location));
|
||||
if (table == null) continue;
|
||||
candidates++;
|
||||
var expected = table.getInstanceType();
|
||||
var actual = program.getListing().getDataAt(location);
|
||||
if (expected != null && actual != null && actual.getDataType().getPathName().equals(expected.getPathName())) annotated++;
|
||||
}
|
||||
coverage.addProperty("frozen_object_candidates", candidates);
|
||||
coverage.addProperty("frozen_objects_annotated", annotated);
|
||||
coverage.addProperty("basis", "rehydrated-pointer-candidates-and-committed-instance-types");
|
||||
return coverage;
|
||||
}
|
||||
|
||||
private JsonObject finish(MessageLog log) {
|
||||
if (!log.toString().isBlank()) diagnostics.add(log.toString());
|
||||
JsonArray messages = new JsonArray();
|
||||
for (String message : diagnostics) messages.add(message);
|
||||
report.add("diagnostics", messages);
|
||||
return report;
|
||||
}
|
||||
|
||||
private JsonObject failure(String status, String reason) {
|
||||
report.addProperty("status", status);
|
||||
report.addProperty("reason", reason);
|
||||
return finish(new MessageLog());
|
||||
}
|
||||
|
||||
static String address(Address value) { return "0x" + Long.toUnsignedString(value.getOffset(), 16); }
|
||||
|
||||
private static String hashMemory(Program program, Address start, long length, TaskMonitor monitor) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] buffer = new byte[65536];
|
||||
for (long offset = 0; offset < length;) {
|
||||
monitor.checkCancelled();
|
||||
int count = (int)Math.min(buffer.length, length - offset);
|
||||
if (program.getMemory().getBytes(start.add(offset), buffer, 0, count) != count)
|
||||
throw new IllegalStateException("Incomplete derived-memory read at " + start.add(offset));
|
||||
digest.update(buffer, 0, count);
|
||||
offset += count;
|
||||
}
|
||||
return java.util.HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/** Keep upstream's unconditional __thiscall assignment out of x64 ABI evidence. */
|
||||
final class NativeAotMethodConventions {
|
||||
static Map<Address, String> capture(Program program, TaskMonitor monitor) throws Exception {
|
||||
Map<Address, String> result = new HashMap<>();
|
||||
var functions = program.getFunctionManager().getFunctions(true);
|
||||
while (functions.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
var function = functions.next();
|
||||
result.put(function.getEntryPoint(), function.getCallingConventionName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void restore(Program program, MethodTableManager manager, Map<Address, String> previous, TaskMonitor monitor) throws Exception {
|
||||
var visited = new HashSet<Address>();
|
||||
String defaultConvention = program.getCompilerSpec().getDefaultCallingConvention().getName();
|
||||
for (var table : manager.getMethodTables()) for (long target : table.getVTable()) {
|
||||
monitor.checkCancelled();
|
||||
Address address = table.getAddress().getNewAddress(target);
|
||||
if (!visited.add(address)) continue;
|
||||
var function = program.getFunctionManager().getFunctionAt(address);
|
||||
if (function == null) continue;
|
||||
String convention = previous.containsKey(address) ? previous.get(address) : defaultConvention;
|
||||
if (!java.util.Objects.equals(function.getCallingConventionName(), convention))
|
||||
function.setCallingConvention(convention);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTable;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
|
||||
/** Translate a selected recovered type into provider-neutral metadata evidence. */
|
||||
final class NativeAotTypeMetadata {
|
||||
static JsonObject describe(Program program, MethodTableManager manager, NativeAotDirectory.Located located,
|
||||
DataType type, JsonObject report, JsonObject derivedRange, TaskMonitor monitor) throws Exception {
|
||||
for (MethodTable table : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
boolean sameType = type.getPathName().equals(table.getMTType().getPathName()) ||
|
||||
(table.getInstanceType() != null && type.getPathName().equals(table.getInstanceType().getPathName()));
|
||||
if (!sameType) continue;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("format", "dotnet-nativeaot");
|
||||
result.addProperty("status", report.get("status").getAsString());
|
||||
result.addProperty("header_address", NativeAotExtension.address(located.address()));
|
||||
result.addProperty("format_major", located.directory().getMajorVersion());
|
||||
result.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
result.addProperty("method_table_address", NativeAotExtension.address(table.getAddress()));
|
||||
result.addProperty("name_origin", table == manager.getObjectMT() || table == manager.getStringMT() ? "inferred" : "generated");
|
||||
result.add("original_name", JsonNull.INSTANCE);
|
||||
result.addProperty("base_size_bytes", table.getBaseSize());
|
||||
result.add("related_type", identity(manager, table.getRelatedTypeAddress()));
|
||||
JsonArray interfaces = new JsonArray();
|
||||
for (long value : table.getInterfaceAddresses()) if (value != 0) interfaces.add(identity(manager, value));
|
||||
result.add("interfaces", interfaces);
|
||||
JsonArray slots = new JsonArray();
|
||||
for (int slot = 0; slot < table.getVTableSlotCount(); slot++) {
|
||||
monitor.checkCancelled();
|
||||
long value = table.getVTableSlot(slot);
|
||||
JsonObject target = new JsonObject();
|
||||
target.addProperty("slot", slot);
|
||||
target.addProperty("slot_address", NativeAotExtension.address(table.getAddress().add(24 + slot * 8L)));
|
||||
target.add("target_address", value == 0 ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive("0x" + Long.toUnsignedString(value, 16)));
|
||||
var function = value == 0 ? null : program.getFunctionManager().getFunctionAt(table.getAddress().getNewAddress(value));
|
||||
target.add("procedure_name", function == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(function.getName(true)));
|
||||
target.addProperty("basis", "method-table-pointer");
|
||||
slots.add(target);
|
||||
}
|
||||
result.add("virtual_slots", slots);
|
||||
result.add("derived_memory", derivedRange.deepCopy());
|
||||
result.add("diagnostics", report.get("diagnostics").deepCopy());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Type identities and relationships are recovered by upstream heuristics. Original source names and custom field layouts are not recovered.");
|
||||
limitations.add("Method prototypes are inferred, not original signatures. REA preserves pre-recovery calling conventions and the loaded compiler default for new functions.");
|
||||
limitations.add("Rehydrated metadata bytes are derived analysis-memory content; their original file offsets and runtime behavior remain unknown.");
|
||||
limitations.add("Verified workflow: .NET 8.0.22 NativeAOT RTR 9.1, Linux x64 ELF / Windows x64 PE on Linux; other runtime layouts and hosts are unverified.");
|
||||
result.add("limitations", limitations);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static com.google.gson.JsonElement identity(MethodTableManager manager, long value) throws Exception {
|
||||
if (value == 0) return JsonNull.INSTANCE;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", "0x" + Long.toUnsignedString(value, 16));
|
||||
MethodTable table = manager.getMethodTable(value);
|
||||
result.add("type", table == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(table.getMTType().getPathName()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+315
-128
@@ -8,12 +8,12 @@ API access on this thread: moving dispatch to a worker can deadlock Hopper.
|
||||
import json
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
from typing import Any, Optional, Protocol, Sequence
|
||||
|
||||
BAD_ADDRESSES = (-1, 0xFFFFFFFFFFFFFFFF, None)
|
||||
_selected_document = None
|
||||
# The native label setter truncates NSString names at this UTF-16 extent.
|
||||
HOPPER_SYMBOL_NAME_UTF16_UNITS = 1024
|
||||
_rea_session_document = None
|
||||
_search_inventory_cache = {}
|
||||
_pseudocode_cache = {}
|
||||
@@ -89,9 +89,11 @@ def _session_document():
|
||||
global _rea_session_document
|
||||
documents = _api().documents()
|
||||
if _rea_session_document is not None:
|
||||
# Hopper recreates Python wrappers; equality compares native handles.
|
||||
# Retain our bound wrapper so cache identity stays stable across calls.
|
||||
return (
|
||||
_rea_session_document
|
||||
if any(document is _rea_session_document for document in documents)
|
||||
if any(document == _rea_session_document for document in documents)
|
||||
else None
|
||||
)
|
||||
return _bind_session_document(documents)
|
||||
@@ -174,46 +176,89 @@ def _json_safe(value):
|
||||
|
||||
|
||||
def _document(name=None):
|
||||
"""Resolve an explicit or session-selected document without changing Hopper UI."""
|
||||
global _selected_document
|
||||
api = _api()
|
||||
documents = api.documents()
|
||||
if name is not None:
|
||||
matches = [candidate for candidate in documents if candidate.getDocumentName() == name]
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
if len(matches) > 1:
|
||||
current = api.current_document()
|
||||
if current is not None and current.getDocumentName() == name:
|
||||
return current
|
||||
raise InvalidRequestError("Hopper document name is ambiguous")
|
||||
raise InvalidRequestError("Unknown Hopper document")
|
||||
if _selected_document is not None:
|
||||
for candidate in documents:
|
||||
if candidate.getDocumentName() == _selected_document:
|
||||
return candidate
|
||||
"""Keep observations and mutations bound to the active REA target."""
|
||||
documents = _api().documents()
|
||||
session_document = _session_document()
|
||||
if session_document is not None:
|
||||
return session_document
|
||||
current = api.current_document()
|
||||
if current is None:
|
||||
raise CapabilityUnavailableError("No Hopper document is loaded")
|
||||
return current
|
||||
if session_document is None:
|
||||
raise CapabilityUnavailableError("The active REA target's Hopper document is no longer open; reopen the target with open_binary")
|
||||
if name is not None:
|
||||
# A document name is display metadata; scope it to the bound native
|
||||
# handle even when another open target happens to have the same name.
|
||||
if session_document.getDocumentName() == name:
|
||||
return session_document
|
||||
if any(candidate.getDocumentName() == name for candidate in documents):
|
||||
raise InvalidRequestError("Hopper document belongs to another target; use open_binary to select that target")
|
||||
raise InvalidRequestError("Unknown Hopper document")
|
||||
return session_document
|
||||
|
||||
|
||||
def _api_text(value, field):
|
||||
"""Validate text before passing it to Hopper's native UTF-8 string API."""
|
||||
if not isinstance(value, str):
|
||||
raise InvalidRequestError("%s must be a string" % field)
|
||||
if "\0" in value:
|
||||
raise InvalidRequestError("%s contains a NUL character, which Hopper's Python API cannot represent" % field)
|
||||
try:
|
||||
value.encode("utf-8")
|
||||
except UnicodeEncodeError as error:
|
||||
raise InvalidRequestError("%s contains an unpaired Unicode surrogate, which Hopper's Python API cannot represent" % field) from error
|
||||
return value
|
||||
|
||||
|
||||
def _validate_name_targets(document, names):
|
||||
"""Keep Hopper's unique-label moves within the explicitly selected addresses."""
|
||||
targets = {address for _, address, _ in names}
|
||||
if len(targets) != len(names):
|
||||
raise InvalidRequestError("Batch rename selects the same address more than once; use one canonical address per target")
|
||||
labels = set()
|
||||
for _, address, name in names:
|
||||
_segment(document, address)
|
||||
if not name:
|
||||
continue
|
||||
if name in labels:
|
||||
raise InvalidRequestError("Batch rename assigns the same name more than once: %s" % name)
|
||||
labels.add(name)
|
||||
owner = document.getAddressForName(name)
|
||||
if owner not in BAD_ADDRESSES and owner != address and owner not in targets:
|
||||
raise InvalidRequestError("Name %s is already assigned to %s; include that address in the batch with a replacement name" % (name, _hex(owner)))
|
||||
|
||||
|
||||
def _symbol_name(value, field):
|
||||
"""Reject native label truncation without altering the caller's name."""
|
||||
value = _api_text(value, field)
|
||||
if len(value.encode("utf-16-le")) // 2 > HOPPER_SYMBOL_NAME_UTF16_UNITS:
|
||||
raise InvalidRequestError("%s exceeds Hopper's %s UTF-16 code-unit symbol-name limit" % (field, HOPPER_SYMBOL_NAME_UTF16_UNITS))
|
||||
return value
|
||||
|
||||
|
||||
def _name_matches(document, address, name):
|
||||
observed = _segment(document, address).getNameAtAddress(address)
|
||||
return observed == name or (not name and observed is None)
|
||||
|
||||
|
||||
def _address(document, value=None):
|
||||
"""Resolve hexadecimal addresses first, then fall back to Hopper symbol names."""
|
||||
"""Keep explicit hexadecimal coordinates distinct from symbol names."""
|
||||
if value is None:
|
||||
return document.getCurrentAddress()
|
||||
if not isinstance(value, str):
|
||||
raise InvalidRequestError("Address must be a string")
|
||||
try:
|
||||
return int(value, 16)
|
||||
except ValueError:
|
||||
result = document.getAddressForName(value)
|
||||
if result in BAD_ADDRESSES:
|
||||
raise InvalidRequestError("Unknown Hopper address or name")
|
||||
return result
|
||||
_api_text(value, "Address")
|
||||
if value.lower().startswith("0x"):
|
||||
try:
|
||||
address = int(value, 16)
|
||||
except ValueError as error:
|
||||
raise InvalidRequestError("Invalid hexadecimal Hopper address: %s" % value) from error
|
||||
else:
|
||||
# Names such as `add` and `face` are valid hexadecimal strings too.
|
||||
# Prefer an observed symbol, retaining bare-hex compatibility only
|
||||
# when Hopper has no symbol with the supplied name.
|
||||
address = document.getAddressForName(value)
|
||||
if address in BAD_ADDRESSES:
|
||||
try:
|
||||
address = int(value, 16)
|
||||
except ValueError as error:
|
||||
raise InvalidRequestError("Unknown Hopper address or name: %s" % value) from error
|
||||
if not 0 <= address < 0xFFFFFFFFFFFFFFFF:
|
||||
raise InvalidRequestError("Address is outside Hopper's usable unsigned 64-bit range: %s" % value)
|
||||
return address
|
||||
|
||||
|
||||
def _segment(document, address):
|
||||
@@ -249,14 +294,18 @@ def _procedure_identity(procedure):
|
||||
|
||||
|
||||
def _procedure_locals(procedure):
|
||||
"""Project opaque Hopper local-variable objects into an exact public shape."""
|
||||
return [
|
||||
{
|
||||
"description": str(local),
|
||||
"""Preserve public local-variable names and observed stack displacements."""
|
||||
result = []
|
||||
for local in procedure.getLocalVariableList():
|
||||
name = local.name()
|
||||
displacement = local.displacement()
|
||||
result.append({
|
||||
"description": "%s (stack displacement %s)" % (name if name is not None else "Unnamed local", displacement),
|
||||
"name": name,
|
||||
"stack_displacement": str(displacement) if displacement is not None else None,
|
||||
"provenance": "hopper-public-python-api",
|
||||
}
|
||||
for local in procedure.getLocalVariableList()
|
||||
]
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def _containing_procedure(document, address):
|
||||
@@ -267,26 +316,75 @@ def _containing_procedure(document, address):
|
||||
return (procedure, None) if procedure is not None else (None, "not_in_procedure")
|
||||
|
||||
|
||||
def _basic_block_end(procedure, block):
|
||||
"""Normalize native block endpoints to an exclusive byte address."""
|
||||
owner_at = getattr(procedure, "getBasicBlockAtAddress", None)
|
||||
if not callable(owner_at):
|
||||
raise CapabilityUnavailableError(
|
||||
"Instruction enumeration requires Hopper basic-block ownership"
|
||||
)
|
||||
end = block.getEndingAddress()
|
||||
# Hopper 6.1 returns the final instruction address, despite the Python
|
||||
# documentation describing an exclusive end. Membership distinguishes
|
||||
# that representation from an actual exclusive endpoint in other builds.
|
||||
if owner_at(end) == block:
|
||||
instruction = procedure.getSegment().getInstructionAtAddress(end)
|
||||
if instruction is not None and instruction.getInstructionLength() > 0:
|
||||
return end + instruction.getInstructionLength()
|
||||
return end
|
||||
|
||||
|
||||
def _block_instruction_addresses(procedure, block):
|
||||
"""Walk either endpoint convention using Hopper's block ownership API.
|
||||
|
||||
Some Hopper builds return the final instruction start, despite the public
|
||||
API documenting an exclusive end. Equality compares fresh native wrappers;
|
||||
probing ownership also keeps an exclusive endpoint in the next block out.
|
||||
"""
|
||||
segment = procedure.getSegment()
|
||||
address = block.getStartingAddress()
|
||||
end = _basic_block_end(procedure, block)
|
||||
while address < end:
|
||||
if procedure.getBasicBlockAtAddress(address) != block:
|
||||
break
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
break
|
||||
length = instruction.getInstructionLength()
|
||||
if length <= 0 or address + length > end:
|
||||
break
|
||||
yield address
|
||||
address += length
|
||||
|
||||
|
||||
def _instruction_addresses(procedure):
|
||||
result = []
|
||||
seen = set()
|
||||
segment = procedure.getSegment()
|
||||
for block in procedure.basicBlockIterator():
|
||||
address = block.getStartingAddress()
|
||||
end = block.getEndingAddress()
|
||||
while address < end and address not in seen:
|
||||
for address in _block_instruction_addresses(procedure, block):
|
||||
if address in seen:
|
||||
continue
|
||||
seen.add(address)
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
break
|
||||
result.append(address)
|
||||
length = instruction.getInstructionLength()
|
||||
if length <= 0:
|
||||
break
|
||||
address += length
|
||||
return result
|
||||
|
||||
|
||||
def _native_calls(procedure, direction):
|
||||
references = procedure.getAllCallees() if direction == "outgoing" else procedure.getAllCallers()
|
||||
kinds = {0: "none", 1: "unknown", 2: "direct", 3: "objective_c"}
|
||||
calls = {}
|
||||
unresolved = []
|
||||
for reference in references:
|
||||
source, target, kind = reference.fromAddress(), reference.toAddress(), reference.type()
|
||||
if source in BAD_ADDRESSES:
|
||||
continue
|
||||
if target in BAD_ADDRESSES:
|
||||
unresolved.append({"address": _hex(source), "reason": "Native CallReference has no resolved target (type %s)" % kind})
|
||||
continue
|
||||
calls[(source, target)] = {"classification": kinds.get(kind, "unknown"), "provider_type": kind, "provenance": "hopper-public-python-api:CallReference"}
|
||||
return calls, sorted(unresolved, key=lambda item: int(item["address"], 16))
|
||||
|
||||
|
||||
def _procedure_references(document, params):
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
direction = params.get("direction", "outgoing")
|
||||
@@ -299,6 +397,8 @@ def _procedure_references(document, params):
|
||||
references = segment.getReferencesFromAddress(address) if direction == "outgoing" else segment.getReferencesOfAddress(address)
|
||||
for reference in references:
|
||||
edges.add((address, reference) if direction == "outgoing" else (reference, address))
|
||||
calls, unresolved = _native_calls(procedure, direction)
|
||||
edges.update(calls)
|
||||
ordered = sorted(edges)
|
||||
items = []
|
||||
for source, target in ordered:
|
||||
@@ -309,16 +409,15 @@ def _procedure_references(document, params):
|
||||
"target_address": _hex(target),
|
||||
"source_procedure": _procedure_identity(source_procedure) if source_procedure is not None else None,
|
||||
"target_procedure": _procedure_identity(target_procedure) if target_procedure is not None else None,
|
||||
"kind": _unavailable("Hopper's public Python API does not classify reference kinds"),
|
||||
"kind": _unavailable("Hopper exposes partial CallReference classification, but not detailed reference flags"),
|
||||
**({"call": calls[(source, target)]} if (source, target) in calls else {}),
|
||||
})
|
||||
return {
|
||||
"procedure": _procedure_identity(procedure), "direction": direction,
|
||||
"reference_kinds_available": False,
|
||||
# The public Hopper API returns observed references but does not expose
|
||||
# enough flow metadata to enumerate calls with no resolved target.
|
||||
# Keep the required list field explicit; the provider records this as
|
||||
# unknown coverage rather than claiming the list is exhaustive.
|
||||
"unresolved_calls": [],
|
||||
# These are native CallReference objects, not proof that every unresolved
|
||||
# dispatch site has been enumerated by Hopper.
|
||||
"unresolved_calls": unresolved,
|
||||
"references": items,
|
||||
}
|
||||
|
||||
@@ -333,11 +432,61 @@ def _procedure_map(document):
|
||||
|
||||
|
||||
def _strings(document):
|
||||
result = {}
|
||||
for segment in document.getSegmentsList():
|
||||
for value, address in segment.getStringsList():
|
||||
result[_hex(address)] = value
|
||||
return result
|
||||
return {address: record["value"] for address, record in _string_inventory(document).items()}
|
||||
|
||||
|
||||
def _string_record(segment, address, display):
|
||||
"""Read one native typed object, retaining its shortened display separately."""
|
||||
length = segment.getObjectLength(address)
|
||||
end = segment.getStartingAddress() + segment.getLength()
|
||||
if isinstance(length, bool) or not isinstance(length, int) or length <= 0 or length > end - address:
|
||||
raise CapabilityUnavailableError("Hopper reported an invalid string object extent at %s" % _hex(address))
|
||||
raw = segment.readBytes(address, length)
|
||||
if not isinstance(raw, (bytes, bytearray)) or len(raw) != length:
|
||||
raise CapabilityUnavailableError("Hopper could not read the complete string object at %s" % _hex(address))
|
||||
kind = segment.getTypeAtAddress(address)
|
||||
encodings = ("utf-8", "latin-1") if kind == segment.TYPE_ASCII else ("utf-16-le", "utf-16-be") if kind == segment.TYPE_UNICODE else ()
|
||||
# Native display escaping is not invertible (a literal backslash-n and a
|
||||
# newline can render alike). Decode byte strings directly; for UTF-16,
|
||||
# compare rendered candidates to establish an unambiguous byte order.
|
||||
candidates = []
|
||||
for encoding in encodings:
|
||||
terminator = b"\x00\x00" if encoding.startswith("utf-16") else b"\x00"
|
||||
terminated = raw.endswith(terminator)
|
||||
payload = raw[:-len(terminator)] if terminated else raw
|
||||
try:
|
||||
value = payload.decode(encoding)
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
record = {"value": value, "provider_value": display, "string": {"encoding": encoding, "encoding_status": "inferred", "termination": "present_or_not_required" if terminated else "missing", "byte_length": length}}
|
||||
if kind == segment.TYPE_ASCII:
|
||||
return record
|
||||
rendered = value.replace('"', '\\"').replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
|
||||
if rendered == display or (display.endswith("\u2026") and display[:-1] and rendered.startswith(display[:-1])):
|
||||
candidates.append(record)
|
||||
if candidates and len({candidate["value"] for candidate in candidates}) == 1:
|
||||
return candidates[0]
|
||||
raise CapabilityUnavailableError("Hopper's string display cannot be reconciled with its typed bytes at %s; inspect the bytes and encoding explicitly" % _hex(address))
|
||||
|
||||
|
||||
def _string_inventory(document, addresses=None):
|
||||
key = (id(document), "string_objects")
|
||||
objects = _search_inventory_cache.get(key)
|
||||
if objects is None:
|
||||
objects = {address: (segment, display) for segment in document.getSegmentsList() for display, address in segment.getStringsList()}
|
||||
_search_inventory_cache[key] = objects
|
||||
key = (id(document), "string_records")
|
||||
records = _search_inventory_cache.setdefault(key, {})
|
||||
selected = sorted(objects if addresses is None else objects.keys() & addresses)
|
||||
for address in selected:
|
||||
name = _hex(address)
|
||||
if name not in records:
|
||||
segment, display = objects[address]
|
||||
try:
|
||||
records[name] = _string_record(segment, address, display)
|
||||
except CapabilityUnavailableError as error:
|
||||
records[name] = {"value": display, "provider_value": display, "decoding": _unavailable(str(error))}
|
||||
return {_hex(address): records[_hex(address)] for address in selected}
|
||||
|
||||
|
||||
def _invalidate_search_inventory(document):
|
||||
@@ -397,17 +546,18 @@ def _search_results(document, kind, params):
|
||||
needle = pattern if case_sensitive else pattern.casefold()
|
||||
matches = lambda value: needle in (value if case_sensitive else value.casefold())
|
||||
else:
|
||||
try:
|
||||
expression = re.compile(pattern, 0 if case_sensitive else re.IGNORECASE)
|
||||
except (re.error, OverflowError) as error:
|
||||
raise InvalidRequestError("Invalid regex pattern") from error
|
||||
matches = lambda value: expression.search(value) is not None
|
||||
raise CapabilityUnavailableError("Regex matching requires REA's supervised worker; the native bridge only executes literal searches")
|
||||
|
||||
selected = []
|
||||
for item in _search_inventory(document, kind):
|
||||
if not matches(item[1]):
|
||||
continue
|
||||
selected.append(item)
|
||||
if not selected:
|
||||
return []
|
||||
if kind == "string":
|
||||
records = _string_inventory(document)
|
||||
return [{"address": address, **records[address]} for address, _ in selected]
|
||||
return [
|
||||
{
|
||||
"address": address,
|
||||
@@ -450,25 +600,11 @@ def _render_instruction(segment, address):
|
||||
|
||||
def _assembly(procedure):
|
||||
"""Render assembly while guarding against malformed instruction cycles."""
|
||||
lines = []
|
||||
segment = procedure.getSegment()
|
||||
seen = set()
|
||||
for block in procedure.basicBlockIterator():
|
||||
address = block.getStartingAddress()
|
||||
end = block.getEndingAddress()
|
||||
while address < end and address not in seen:
|
||||
seen.add(address)
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
break
|
||||
line = _render_instruction(segment, address)
|
||||
if line is not None:
|
||||
lines.append(line)
|
||||
length = instruction.getInstructionLength()
|
||||
if length <= 0:
|
||||
break
|
||||
address += length
|
||||
return "\n".join(lines)
|
||||
return "\n".join(
|
||||
line for address in _instruction_addresses(procedure)
|
||||
if (line := _render_instruction(segment, address)) is not None
|
||||
)
|
||||
|
||||
|
||||
def _read_function_instructions(document, params):
|
||||
@@ -505,7 +641,7 @@ def _analyze_function(document, params):
|
||||
successors.append(_hex(successor))
|
||||
blocks.append({
|
||||
"start": _hex(block.getStartingAddress()),
|
||||
"end": _hex(block.getEndingAddress()),
|
||||
"end": _hex(_basic_block_end(procedure, block)),
|
||||
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
|
||||
})
|
||||
pseudo = _pseudocode(document, procedure) or ""
|
||||
@@ -529,6 +665,10 @@ def _analyze_function(document, params):
|
||||
incoming = []
|
||||
outgoing = []
|
||||
procedure_addresses = set(addresses)
|
||||
outgoing_calls, unresolved_calls = _native_calls(procedure, "outgoing")
|
||||
incoming_calls, _ = _native_calls(procedure, "incoming")
|
||||
calls = {**incoming_calls, **outgoing_calls}
|
||||
edges.update(calls)
|
||||
for source, target in sorted(edges):
|
||||
source_procedure, _ = _containing_procedure(document, source)
|
||||
target_procedure, _ = _containing_procedure(document, target)
|
||||
@@ -537,16 +677,14 @@ def _analyze_function(document, params):
|
||||
"target_address": _hex(target),
|
||||
"source_procedure": _procedure_identity(source_procedure) if source_procedure is not None else None,
|
||||
"target_procedure": _procedure_identity(target_procedure) if target_procedure is not None else None,
|
||||
"kind": _unavailable("Hopper's public Python API does not classify reference kinds"),
|
||||
"kind": _unavailable("Hopper exposes partial CallReference classification, but not detailed reference flags"),
|
||||
**({"call": calls[(source, target)]} if (source, target) in calls else {}),
|
||||
}
|
||||
if target in procedure_addresses and source not in procedure_addresses:
|
||||
incoming.append(item)
|
||||
if source in procedure_addresses:
|
||||
outgoing.append(item)
|
||||
string_map = {
|
||||
int(address, 16): value
|
||||
for address, value in _search_inventory(document, "string")
|
||||
}
|
||||
string_map = {int(address, 16): record for address, record in _string_inventory(document, {int(edge["target_address"], 16) for edge in outgoing}).items()}
|
||||
name_map = {
|
||||
int(address, 16): value
|
||||
for address, value in _search_inventory(document, "name")
|
||||
@@ -556,7 +694,7 @@ def _analyze_function(document, params):
|
||||
for edge in outgoing:
|
||||
target = int(edge["target_address"], 16)
|
||||
if target in string_map:
|
||||
referenced_strings.append({"address": edge["target_address"], "value": string_map[target], "source_address": edge["source_address"]})
|
||||
referenced_strings.append({"address": edge["target_address"], **string_map[target], "source_address": edge["source_address"]})
|
||||
if target in name_map:
|
||||
referenced_names.append({"address": edge["target_address"], "value": name_map[target], "source_address": edge["source_address"]})
|
||||
comments.sort(key=lambda item: (int(item["address"], 16), item["kind"]))
|
||||
@@ -574,13 +712,14 @@ def _analyze_function(document, params):
|
||||
"callers": callers, "callees": callees,
|
||||
"incoming_references": incoming,
|
||||
"outgoing_references": outgoing,
|
||||
"unresolved_calls": unresolved_calls,
|
||||
"referenced_strings": referenced_strings,
|
||||
"referenced_names": referenced_names,
|
||||
"basic_blocks": blocks,
|
||||
"limitations": [
|
||||
"Hopper's public Python API does not classify reference kinds.",
|
||||
"Native CallReference classifications are observed where available; detailed reference flags remain unknown.",
|
||||
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
|
||||
"Unresolved indirect calls without reported target addresses are not represented as call edges.",
|
||||
"Native calls without resolved target addresses are not represented as edges; enumeration beyond reported CallReference objects is unknown.",
|
||||
"Pseudocode and assembly are provider-specific representations, not original source.",
|
||||
],
|
||||
}
|
||||
@@ -588,7 +727,6 @@ def _analyze_function(document, params):
|
||||
|
||||
def _dispatch(method, params):
|
||||
"""Dispatch only the closed operation set implemented by REA's public tools."""
|
||||
global _selected_document
|
||||
if method == "health":
|
||||
return {"name": "REA Hopper bridge", "version": "1.0.0", "run_id": REA_RUN_ID}
|
||||
if method in ("shutdown", "shutdown_document"):
|
||||
@@ -622,14 +760,9 @@ def _dispatch(method, params):
|
||||
"document_closed": document_closed,
|
||||
}
|
||||
if method == "list_documents":
|
||||
return [document.getDocumentName() for document in Document.getAllDocuments()]
|
||||
return [document.getDocumentName() for document in _api().documents()]
|
||||
if method == "current_document":
|
||||
return _document().getDocumentName()
|
||||
if method == "set_current_document":
|
||||
document = _document(params.get("document"))
|
||||
_selected_document = document.getDocumentName()
|
||||
return _selected_document
|
||||
|
||||
document = _document(params.get("document"))
|
||||
if method in EXHAUSTIVE_ANALYSIS_METHODS:
|
||||
_api().require_analysis_complete(document, method)
|
||||
@@ -650,10 +783,17 @@ def _dispatch(method, params):
|
||||
"Hopper's public Python API does not expose readBytes"
|
||||
)
|
||||
address = _address(document, params.get("address"))
|
||||
value = reader(address, length)
|
||||
segment = _segment(document, address)
|
||||
# Document.readBytes delegates to one Segment and returns False when
|
||||
# the requested range crosses its end. Preserve the readable prefix.
|
||||
available = segment.getStartingAddress() + segment.getLength() - address
|
||||
value = reader(address, min(length, available))
|
||||
if value is False or value is None:
|
||||
raise InvalidRequestError("Hopper could not read the requested byte range at %s" % _hex(address))
|
||||
if not isinstance(value, (bytes, bytearray)):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API returned an unsupported byte representation"
|
||||
"Hopper readBytes returned %s for the requested %s-byte range at %s"
|
||||
% (type(value).__name__, length, _hex(address))
|
||||
)
|
||||
data = bytes(value)
|
||||
return {
|
||||
@@ -678,7 +818,26 @@ def _dispatch(method, params):
|
||||
or offset < 0
|
||||
):
|
||||
raise InvalidRequestError("Address has no authoritative file-offset mapping")
|
||||
return {"address": _hex(address), "file_offset": offset}
|
||||
inverse = getattr(document, "getAddressFromFileOffset", None)
|
||||
if not callable(inverse):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API cannot verify the file-offset mapping by reverse lookup"
|
||||
)
|
||||
# Synthetic memory can report offset zero, which belongs to the actual
|
||||
# file header. An integer alone does not prove source-file provenance.
|
||||
if inverse(offset) != address:
|
||||
raise InvalidRequestError("Address has no authoritative file-offset mapping: reverse lookup disagrees at %s" % _hex(address))
|
||||
if offset > 9007199254740991:
|
||||
raise CapabilityUnavailableError("Provider file offset exceeds the exact JSON integer range")
|
||||
header_address = inverse(0)
|
||||
reader = getattr(document, "readBytes", None)
|
||||
header = reader(header_address, 12) if header_address not in BAD_ADDRESSES and document.getSegmentAtAddress(header_address) is not None and callable(reader) else None
|
||||
return {
|
||||
"address": _hex(address),
|
||||
"provider_file_offset": offset,
|
||||
"provider_source_path": document.getExecutableFilePath(),
|
||||
"provider_image_header_hex": bytes(header).hex() if isinstance(header, (bytes, bytearray)) and len(header) == 12 else None,
|
||||
}
|
||||
if method == "resolve_containing_procedure":
|
||||
address = _address(document, params.get("address"))
|
||||
procedure, reason = _containing_procedure(document, address)
|
||||
@@ -694,8 +853,13 @@ def _dispatch(method, params):
|
||||
return _procedure_name(_procedure(document))
|
||||
if method == "goto_address":
|
||||
address = _address(document, params.get("address"))
|
||||
segment = _segment(document, address)
|
||||
expected = segment.getInstructionStart(address)
|
||||
document.moveCursorAtAddress(address)
|
||||
return _hex(address)
|
||||
observed = document.getCurrentAddress()
|
||||
if observed != (address if expected in BAD_ADDRESSES else expected):
|
||||
raise InvalidRequestError("Hopper cursor did not reach the requested object at %s; observed %s" % (_hex(address), _hex(observed)))
|
||||
return _hex(observed)
|
||||
if method in ("address_name", "comment", "inline_comment", "xrefs"):
|
||||
target = _address(document, params.get("address"))
|
||||
segment = _segment(document, target)
|
||||
@@ -708,12 +872,22 @@ def _dispatch(method, params):
|
||||
return [_hex(value) for value in segment.getReferencesOfAddress(target)]
|
||||
if method in ("next_address", "prev_address"):
|
||||
target = _address(document, params.get("address"))
|
||||
segment = _segment(document, target)
|
||||
if method == "next_address":
|
||||
result = target + max(1, document.getObjectLength(target))
|
||||
start = segment.getInstructionStart(target)
|
||||
if start in BAD_ADDRESSES:
|
||||
raise InvalidRequestError("No analyzed object exists at the requested address")
|
||||
length = segment.getObjectLength(start)
|
||||
if length in BAD_ADDRESSES or length <= 0:
|
||||
raise InvalidRequestError("No next analyzed object exists at the requested address")
|
||||
result = start + length
|
||||
else:
|
||||
result = document.getInstructionStart(max(0, target - 1))
|
||||
if result in BAD_ADDRESSES:
|
||||
raise InvalidRequestError("No adjacent address")
|
||||
previous_segment = document.getSegmentAtAddress(target - 1) if target > 0 else None
|
||||
if previous_segment is None:
|
||||
raise InvalidRequestError("No previous address exists in mapped memory")
|
||||
result = previous_segment.getInstructionStart(target - 1)
|
||||
if result in BAD_ADDRESSES or document.getSegmentAtAddress(result) is None:
|
||||
raise InvalidRequestError("No adjacent analyzed object exists in mapped memory")
|
||||
return _hex(result)
|
||||
if method == "list_segments":
|
||||
result = []
|
||||
@@ -747,12 +921,9 @@ def _dispatch(method, params):
|
||||
if method == "list_procedures":
|
||||
return [{"address": address, "value": value} for address, value in _search_inventory(document, "procedure")]
|
||||
if method == "list_strings":
|
||||
values = dict(_search_inventory(document, "string"))
|
||||
requested = params.get("address")
|
||||
if requested is not None:
|
||||
key = _hex(_address(document, requested))
|
||||
values = {key: values[key]} if key in values else {}
|
||||
return [{"address": address, "value": value} for address, value in values.items()]
|
||||
values = _string_inventory(document, None if requested is None else {_address(document, requested)})
|
||||
return [{"address": address, **record} for address, record in values.items()]
|
||||
if method == "list_names":
|
||||
result = dict(_search_inventory(document, "name"))
|
||||
requested = params.get("address")
|
||||
@@ -777,7 +948,7 @@ def _dispatch(method, params):
|
||||
return sorted((_hex(item.getEntryPoint()) for item in procedure.getAllCalleeProcedures()), key=lambda value: int(value, 16))
|
||||
if method == "procedure_info":
|
||||
blocks = list(procedure.basicBlockIterator())
|
||||
length = sum(max(0, block.getEndingAddress() - block.getStartingAddress()) for block in blocks)
|
||||
length = sum(max(0, _basic_block_end(procedure, block) - block.getStartingAddress()) for block in blocks)
|
||||
return {
|
||||
"name": _procedure_name(procedure),
|
||||
"entrypoint": _hex(procedure.getEntryPoint()),
|
||||
@@ -790,35 +961,51 @@ def _dispatch(method, params):
|
||||
}
|
||||
if method == "set_address_name":
|
||||
address = _address(document, params.get("address"))
|
||||
name = _symbol_name(params["name"], "Name")
|
||||
_validate_name_targets(document, [(params.get("address"), address, name)])
|
||||
try:
|
||||
result = document.setNameAtAddress(address, params["name"])
|
||||
result = document.setNameAtAddress(address, name)
|
||||
finally:
|
||||
_invalidate_search_inventory(document)
|
||||
_invalidate_pseudocode(document)
|
||||
return result
|
||||
return bool(result) and _name_matches(document, address, name)
|
||||
if method == "set_addresses_names":
|
||||
# Validate all destinations before applying any annotation. A malformed
|
||||
# later address must not discard the result of an earlier mutation.
|
||||
names = [(key, _address(document, key), _symbol_name(value, "Name for %s" % key)) for key, value in params["names"].items()]
|
||||
_validate_name_targets(document, names)
|
||||
try:
|
||||
result = {key: document.setNameAtAddress(_address(document, key), value) for key, value in params["names"].items()}
|
||||
result = {key: document.setNameAtAddress(address, value) for key, address, value in names}
|
||||
finally:
|
||||
_invalidate_search_inventory(document)
|
||||
_invalidate_pseudocode(document)
|
||||
return result
|
||||
return {key: bool(result[key]) and _name_matches(document, address, value) for key, address, value in names}
|
||||
if method in ("set_comment", "set_inline_comment"):
|
||||
address = _address(document, params.get("address"))
|
||||
comment = _api_text(params["comment"], "Comment")
|
||||
segment = _segment(document, address)
|
||||
setter = segment.setCommentAtAddress if method == "set_comment" else segment.setInlineCommentAtAddress
|
||||
getter = segment.getCommentAtAddress if method == "set_comment" else segment.getInlineCommentAtAddress
|
||||
setter(address, params["comment"])
|
||||
setter(address, comment)
|
||||
_invalidate_pseudocode(document)
|
||||
return getter(address) == params["comment"]
|
||||
observed = getter(address)
|
||||
return observed == comment or (comment == "" and observed is None)
|
||||
if method == "list_bookmarks":
|
||||
return [{"address": _hex(item), "name": document.getBookmarkName(item)} for item in document.getBookmarks()]
|
||||
if method == "set_bookmark":
|
||||
address = _address(document, params.get("address"))
|
||||
document.setBookmarkAtAddress(address, params.get("name"))
|
||||
_segment(document, address)
|
||||
name = params.get("name")
|
||||
if name is not None:
|
||||
_api_text(name, "Bookmark name")
|
||||
document.setBookmarkAtAddress(address, name)
|
||||
return document.hasBookmarkAtAddress(address)
|
||||
if method == "unset_bookmark":
|
||||
address = _address(document, params.get("address"))
|
||||
# Permit removing a legacy orphan bookmark, but do not report success
|
||||
# for an unmapped address with no bookmark to remove.
|
||||
if not document.hasBookmarkAtAddress(address):
|
||||
_segment(document, address)
|
||||
document.removeBookmarkAtAddress(address)
|
||||
return not document.hasBookmarkAtAddress(address)
|
||||
raise InvalidRequestError("Unknown bridge method")
|
||||
@@ -913,14 +1100,14 @@ def _diagnostic_type(error):
|
||||
|
||||
|
||||
def _safe_diagnostic(error):
|
||||
"""Project one exception without retaining provider or credential text."""
|
||||
"""Preserve bridge validation reasons while excluding transport credentials."""
|
||||
diagnostic_type = _diagnostic_type(error)
|
||||
if diagnostic_type == "capability_unavailable":
|
||||
message = str(error)
|
||||
elif diagnostic_type == "authorization":
|
||||
message = "Invalid bridge capability"
|
||||
elif diagnostic_type == "invalid_request":
|
||||
message = "Invalid Hopper bridge request"
|
||||
message = str(error)
|
||||
else:
|
||||
message = "%s: Hopper bridge operation failed" % type(error).__name__
|
||||
return {"code": -32000, "message": message, "type": diagnostic_type}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
"""REA offline adapter using mitmproxy's unchanged native tnetstring decoder.
|
||||
|
||||
No FlowReader migrations, HAR conversion, proxy listener, or target requests.
|
||||
The containing mitmdump process and its configuration directory are REA-owned.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import resource
|
||||
from pathlib import Path
|
||||
|
||||
from mitmproxy import ctx, version
|
||||
from mitmproxy.io import tnetstring
|
||||
|
||||
PROFILE = "12.2.3"
|
||||
OUTPUT_BYTES = 96 * 1024 * 1024
|
||||
CREDENTIALS = {b"authorization", b"proxy-authorization", b"cookie", b"set-cookie"}
|
||||
|
||||
|
||||
class CaptureFailure(Exception):
|
||||
def __init__(self, reason, message, pointer=""):
|
||||
self.reason, self.pointer = reason, pointer
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def pointer(parent, key):
|
||||
return parent + "/" + str(key).replace("~", "~0").replace("/", "~1")
|
||||
|
||||
|
||||
class BoundedReader:
|
||||
"""Refuse malicious declared lengths before the upstream parser allocates."""
|
||||
def __init__(self, handle, size):
|
||||
self.handle, self.size = handle, size
|
||||
|
||||
def read(self, count):
|
||||
if count < 0 or count > self.size - self.handle.tell():
|
||||
raise CaptureFailure("format", "Native record declares bytes beyond the retained capture.")
|
||||
return self.handle.read(count)
|
||||
|
||||
|
||||
def project_record(state, secrets):
|
||||
binaries, numbers, redactions = [], [], []
|
||||
byte_secrets = []
|
||||
for secret in secrets:
|
||||
try:
|
||||
byte_secrets.append(secret.encode("utf-8", errors="strict"))
|
||||
except UnicodeEncodeError:
|
||||
# Non-scalar JSON strings have no UTF-8 byte representation.
|
||||
# Keep them in secrets for the ordinary string projection.
|
||||
pass
|
||||
|
||||
def redact(text, path, original):
|
||||
if any(secret in text or secret in original for secret in secrets):
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
return None
|
||||
return text
|
||||
|
||||
def transport_value(value, path, transport_url):
|
||||
if not (transport_url or re.fullmatch(r"/(?:backup/)*request/(?:path|authority)", path)):
|
||||
return value, False
|
||||
binary = isinstance(value, bytes)
|
||||
pattern = rb"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@" if binary else r"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@"
|
||||
replacement = rb"\1" if binary else r"\1"
|
||||
separator = b"@" if binary else "@"
|
||||
safe_url = re.sub(pattern, replacement, value, flags=re.I)
|
||||
if path.endswith("/authority") and separator in safe_url:
|
||||
safe_url = safe_url.rsplit(separator, 1)[1]
|
||||
changed = safe_url != value
|
||||
if changed:
|
||||
redactions.append({"pointer": path, "reason": "transport-credential"})
|
||||
return safe_url, changed
|
||||
|
||||
def validate_omitted(value, path, depth):
|
||||
before = len(binaries), len(numbers), len(redactions)
|
||||
visit(value, path, depth)
|
||||
del binaries[before[0]:]
|
||||
del numbers[before[1]:]
|
||||
del redactions[before[2]:]
|
||||
|
||||
def visit(value, path, depth=0, credential=False, transport_url=False):
|
||||
if depth > 64:
|
||||
raise CaptureFailure("input-limit", "Native capture exceeds the 64-level evidence nesting budget.", path)
|
||||
if credential:
|
||||
validate_omitted(value, path, depth)
|
||||
redactions.append({"pointer": path, "reason": "transport-credential"})
|
||||
if isinstance(value, bytes):
|
||||
binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted", "content_base64": None, "bytes": None, "sha256": None})
|
||||
return None
|
||||
if isinstance(value, bytes):
|
||||
safe_bytes, credential_url = transport_value(value, path, transport_url)
|
||||
content_base64 = base64.b64encode(value).decode("ascii")
|
||||
digest = hashlib.sha256(value).hexdigest()
|
||||
hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest for secret in secrets)
|
||||
safe_text = None
|
||||
try:
|
||||
safe_text = safe_bytes.decode("utf-8", errors="strict")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
if hidden:
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
exclude = hidden or credential_url
|
||||
binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted" if exclude else "retained", "content_base64": None if exclude else content_base64, "bytes": None if exclude else len(value), "sha256": None if exclude else digest})
|
||||
if hidden:
|
||||
return None
|
||||
return safe_text
|
||||
if isinstance(value, str):
|
||||
safe_text, _ = transport_value(value, path, transport_url)
|
||||
return redact(safe_text, path, value)
|
||||
if isinstance(value, bool) or value is None:
|
||||
return value
|
||||
if isinstance(value, (int, float)):
|
||||
literal = repr(value)
|
||||
if any(secret in literal for secret in secrets):
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
return None
|
||||
numbers.append({"pointer": path, "producer_type": "integer" if isinstance(value, int) else "float", "literal": literal})
|
||||
return value if (abs(value) <= 9007199254740991 if isinstance(value, int) else math.isfinite(value)) else None
|
||||
if isinstance(value, (list, tuple)):
|
||||
fields = re.fullmatch(r"/(?:backup/)*(?:request|response)/(?:headers|trailers)", path)
|
||||
if fields:
|
||||
result = []
|
||||
for index, field in enumerate(value):
|
||||
if not isinstance(field, (list, tuple)) or len(field) != 2 or not isinstance(field[0], bytes):
|
||||
raise CaptureFailure("format", "Native header/trailer is not an ordered byte pair.", pointer(path, index))
|
||||
result.append([visit(field[0], pointer(pointer(path, index), 0), depth + 2), visit(field[1], pointer(pointer(path, index), 1), depth + 2, field[0].lower() in CREDENTIALS, field[0].lower() in {b"location", b"referer", b"origin"})])
|
||||
return result
|
||||
return [visit(item, pointer(path, index), depth + 1) for index, item in enumerate(value)]
|
||||
if isinstance(value, dict):
|
||||
result = {}
|
||||
for key, item in value.items():
|
||||
if not isinstance(key, str):
|
||||
raise CaptureFailure("format", "Native record has a non-string dictionary key.", path)
|
||||
if any(secret in key for secret in secrets):
|
||||
# Validate the omitted subtree too, then discard all of its
|
||||
# coordinates rather than inventing a replacement identity.
|
||||
validate_omitted(item, pointer(path, key), depth + 1)
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value", "scope": "property-name"})
|
||||
continue
|
||||
result[key] = visit(item, pointer(path, key), depth + 1)
|
||||
return result
|
||||
raise CaptureFailure("decoder", "Native parser returned an unexpected value type: " + type(value).__name__ + ".", path)
|
||||
|
||||
return {"reported": visit(state, ""), "binary_fields": binaries, "numeric_literals": numbers, "redactions": redactions}
|
||||
|
||||
|
||||
def decode(request):
|
||||
if version.VERSION != PROFILE:
|
||||
raise CaptureFailure("unsupported", "Expected mitmproxy " + PROFILE + "; observed " + version.VERSION + ".")
|
||||
records = []
|
||||
capture = Path(request["snapshot_path"])
|
||||
size = capture.stat().st_size
|
||||
with capture.open("rb") as handle:
|
||||
reader = BoundedReader(handle, size)
|
||||
while handle.tell() < size:
|
||||
start = handle.tell()
|
||||
try:
|
||||
state = tnetstring.load(reader)
|
||||
except CaptureFailure:
|
||||
raise
|
||||
except RecursionError:
|
||||
raise CaptureFailure("input-limit", "Native parser exceeded its recursion budget at byte offset " + str(start) + ".") from None
|
||||
except (ValueError, IndexError, UnicodeError, EOFError):
|
||||
raise CaptureFailure("format", "Malformed native record at byte offset " + str(start) + ".") from None
|
||||
if not isinstance(state, dict):
|
||||
raise CaptureFailure("format", "Native flow record must be a dictionary at byte offset " + str(start) + ".")
|
||||
records.append({"ordinal": len(records), "location": {"kind": "byte-range", "offset": start, "bytes": handle.tell() - start}, **project_record(state, request["sensitive_values"]), "limitations": ["Fields are original native states without FlowReader migration. Binary fields contain exact producer bytes; reported UTF-8 strings for those fields are derived display views. Unknown flow/version extensions are retained without interpretation."]})
|
||||
return {"decoder": {"id": "mitmproxy-native-tnetstring", "name": "REA offline native mitmproxy adapter", "version": PROFILE}, "container": {"reported": None, "numeric_literals": [], "redactions": [], "records_pointer": None}, "total_records": len(records), "records": records}
|
||||
|
||||
|
||||
def failure_reply(error):
|
||||
"""Keep producer constraints, resource exhaustion and adapter defects distinct."""
|
||||
if isinstance(error, CaptureFailure):
|
||||
return {"ok": False, "reason": error.reason, "message": str(error), "pointer": error.pointer}
|
||||
if isinstance(error, MemoryError):
|
||||
return {"ok": False, "reason": "resource-limit", "message": "Native decoder exhausted its 768 MiB address-space budget.", "pointer": ""}
|
||||
return {"ok": False, "reason": "decoder", "message": "Offline native decoder failed unexpectedly: " + type(error).__name__ + ".", "pointer": ""}
|
||||
|
||||
|
||||
class OfflineCapture:
|
||||
def load(self, loader):
|
||||
loader.add_option("rea_request_path", str, "", "REA private offline capture request")
|
||||
|
||||
def running(self):
|
||||
resource.setrlimit(resource.RLIMIT_AS, (768 * 1024 * 1024, 768 * 1024 * 1024))
|
||||
resource.setrlimit(resource.RLIMIT_CPU, (30, 30))
|
||||
resource.setrlimit(resource.RLIMIT_FSIZE, (OUTPUT_BYTES, OUTPUT_BYTES))
|
||||
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
|
||||
request = json.loads(Path(ctx.options.rea_request_path).read_text(encoding="utf-8"))
|
||||
try:
|
||||
response = {"ok": True, "value": decode(request)}
|
||||
except Exception as error:
|
||||
response = failure_reply(error)
|
||||
try:
|
||||
try:
|
||||
output = json.dumps(response, ensure_ascii=True, allow_nan=False, separators=(",", ":")).encode("ascii")
|
||||
except Exception as error:
|
||||
output = json.dumps(failure_reply(error), separators=(",", ":")).encode("ascii")
|
||||
if len(output) > OUTPUT_BYTES:
|
||||
output = b'{"ok":false,"reason":"limit","message":"Native reply exceeds the complete-evidence output budget.","pointer":""}'
|
||||
Path(request["reply_path"]).write_bytes(output)
|
||||
finally:
|
||||
ctx.master.shutdown()
|
||||
|
||||
|
||||
addons = [OfflineCapture()]
|
||||
@@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
|
||||
struct ChildBatch<Element> {
|
||||
let values: [Element]
|
||||
let complete: Bool
|
||||
}
|
||||
|
||||
struct ChildCount {
|
||||
let value: Int?
|
||||
let error: Int32?
|
||||
}
|
||||
|
||||
func serializeHelperOutput(_ value: Any) throws -> Data {
|
||||
try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys, .withoutEscapingSlashes])
|
||||
}
|
||||
|
||||
func captureChildCount(status: Int32, success: Int32, value: Int) -> ChildCount {
|
||||
guard status == success else { return ChildCount(value: nil, error: status) }
|
||||
return ChildCount(value: value, error: nil)
|
||||
}
|
||||
|
||||
func captureChildValues<Element>(requestedCount: Int, copy: () -> [Element]?) -> ChildBatch<Element> {
|
||||
guard requestedCount > 0 else { return ChildBatch(values: [], complete: true) }
|
||||
let returned = copy() ?? []
|
||||
let values = Array(returned.prefix(requestedCount))
|
||||
return ChildBatch(values: values, complete: values.count == requestedCount)
|
||||
}
|
||||
@@ -22,15 +22,17 @@ func attribute(_ element: AXUIElement, _ key: String) -> CFTypeRef? {
|
||||
var value: CFTypeRef?
|
||||
return AXUIElementCopyAttributeValue(element, key as CFString, &value) == .success ? value : nil
|
||||
}
|
||||
func childCount(_ element: AXUIElement) -> Int {
|
||||
func childCount(_ element: AXUIElement) -> ChildCount {
|
||||
var count = 0
|
||||
return AXUIElementGetAttributeValueCount(element, kAXChildrenAttribute as CFString, &count) == .success ? count : 0
|
||||
let status = AXUIElementGetAttributeValueCount(element, kAXChildrenAttribute as CFString, &count)
|
||||
return captureChildCount(status: status.rawValue, success: AXError.success.rawValue, value: count)
|
||||
}
|
||||
func children(_ element: AXUIElement, limit: Int) -> [AXUIElement] {
|
||||
var value: CFArray?
|
||||
let count = min(limit, childCount(element))
|
||||
if count == 0 { return [] }
|
||||
return AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, 0, count, &value) == .success ? value as? [AXUIElement] ?? [] : []
|
||||
func children(_ element: AXUIElement, count: Int) -> ChildBatch<AXUIElement> {
|
||||
captureChildValues(requestedCount: count) {
|
||||
var value: CFArray?
|
||||
guard AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, 0, count, &value) == .success else { return nil }
|
||||
return value as? [AXUIElement]
|
||||
}
|
||||
}
|
||||
func text(_ element: AXUIElement, _ key: String) -> Any { (attribute(element, key) as? String) ?? NSNull() as Any }
|
||||
func windowBounds(_ element: AXUIElement) -> CGRect? {
|
||||
@@ -87,7 +89,11 @@ func observe(_ request: Request) async throws -> [String: Any] {
|
||||
var element = root
|
||||
for index in action.path ?? [] {
|
||||
var selectedChild: CFArray?
|
||||
guard index >= 0 && index < childCount(element),
|
||||
let count = childCount(element)
|
||||
guard let available = count.value else {
|
||||
try fail("element-count-unavailable", "Cannot validate selected accessibility path because child count failed with AXError \(count.error ?? -1)")
|
||||
}
|
||||
guard index >= 0 && index < available,
|
||||
AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, index, 1, &selectedChild) == .success,
|
||||
let item = (selectedChild as? [AXUIElement])?.first else { try fail("element-missing", "Selected accessibility path no longer exists") }
|
||||
element = item
|
||||
@@ -103,21 +109,29 @@ func observe(_ request: Request) async throws -> [String: Any] {
|
||||
}
|
||||
guard outcome == .success else { try fail("action-failed", "Accessibility action failed with AXError \(outcome.rawValue)") }
|
||||
}
|
||||
var nodes: [[String: Any]] = []; var truncated = false
|
||||
var nodes: [[String: Any]] = []; var truncated = false; var gaps: [String] = []
|
||||
if request.accessibility, let root = selected {
|
||||
var pending: [(AXUIElement, [Int])] = [(root, [])]
|
||||
while let (element, path) = pending.popLast() {
|
||||
if nodes.count >= request.max_nodes { truncated = true; break }
|
||||
let totalChildren = childCount(element)
|
||||
let childCountResult = childCount(element)
|
||||
var actions: CFArray?
|
||||
AXUIElementCopyActionNames(element, &actions)
|
||||
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": text(element, kAXValueAttribute), "actions": actions as? [String] ?? [], "children_count": totalChildren])
|
||||
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": text(element, kAXValueAttribute), "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
|
||||
guard let totalChildren = childCountResult.value else {
|
||||
truncated = true
|
||||
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
|
||||
continue
|
||||
}
|
||||
if path.count >= 32 { if totalChildren > 0 { truncated = true }; continue }
|
||||
let available = max(0, request.max_nodes - nodes.count - pending.count)
|
||||
let count = min(available, totalChildren)
|
||||
let items = children(element, limit: count)
|
||||
let batch = children(element, count: count)
|
||||
if !batch.complete { truncated = true }
|
||||
if totalChildren > count { truncated = true }
|
||||
for index in (0..<count).reversed() { pending.append((items[index], path + [index])) }
|
||||
for (index, item) in batch.values.enumerated().reversed() {
|
||||
pending.append((item, path + [index]))
|
||||
}
|
||||
}
|
||||
}
|
||||
var screenshot: Any = NSNull()
|
||||
@@ -139,18 +153,18 @@ func observe(_ request: Request) async throws -> [String: Any] {
|
||||
}
|
||||
screenshot = ["mime_type": "image/png", "base64": png.base64EncodedString(), "sha256": SHA256.hash(data: png).map({ String(format: "%02x", $0) }).joined(), "width": image.width, "height": image.height]
|
||||
}
|
||||
return ["window": ["pid": request.pid, "window_id": request.window_id, "executable": request.executable, "launch_time": launch, "title": window[kCGWindowName as String] as? String ?? ""], "nodes": nodes, "truncated": truncated, "screenshot": screenshot, "gaps": request.accessibility ? [] : ["Accessibility capture was disabled"]]
|
||||
return ["window": ["pid": request.pid, "window_id": request.window_id, "executable": request.executable, "launch_time": launch, "title": window[kCGWindowName as String] as? String ?? ""], "nodes": nodes, "truncated": truncated, "screenshot": screenshot, "gaps": request.accessibility ? gaps : ["Accessibility capture was disabled"]]
|
||||
}
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let request = try JSONDecoder().decode(Request.self, from: Data(CommandLine.arguments[1].utf8))
|
||||
let result = try await observe(request)
|
||||
let output = try JSONSerialization.data(withJSONObject: ["ok": true, "result": result], options: [.sortedKeys])
|
||||
let output = try serializeHelperOutput(["ok": true, "result": result])
|
||||
FileHandle.standardOutput.write(output)
|
||||
} catch {
|
||||
let failure = error as? BoundaryFailure
|
||||
let output: [String: Any] = ["ok": false, "code": failure?.code ?? "capture-failed", "message": failure?.message ?? error.localizedDescription]
|
||||
if let data = try? JSONSerialization.data(withJSONObject: output, options: [.sortedKeys]) { FileHandle.standardOutput.write(data) }
|
||||
if let data = try? serializeHelperOutput(output) { FileHandle.standardOutput.write(data) }
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,839 @@
|
||||
"""Bounded native call observation executed inside LLDB's embedded Python.
|
||||
|
||||
REA imports this module into `lldb --batch` and runs `rea_trace <config.json>`.
|
||||
It launches one owned process, stops at the requested functions and
|
||||
Objective-C methods, records each entry from registers and memory reads only,
|
||||
continues, and kills the process once it exits, the event or aggregate resource
|
||||
limit is reached, or the duration elapses. Target output is drained through
|
||||
bounded FIFOs. No expression is evaluated and no target code runs on the
|
||||
tracer's behalf. The result is written as JSON to the configured path.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import select
|
||||
import threading
|
||||
import time
|
||||
|
||||
import lldb
|
||||
|
||||
ARGUMENT_REGISTERS = {
|
||||
"arm64": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
|
||||
"arm64e": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
|
||||
"x86_64": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
|
||||
"x86_64h": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
|
||||
}
|
||||
MAX_SELECTOR_BYTES = 1024
|
||||
MAX_REPORTED_LOCATIONS = 64
|
||||
MAX_OTHER_STOPS = 256
|
||||
MAX_TRACE_BYTES = 8 * 1024 * 1024
|
||||
MAX_TRACE_FRAMES = 65_536
|
||||
MAX_OBSERVATION_JOURNAL_BYTES = MAX_TRACE_BYTES
|
||||
MAX_BREAKPOINT_LOCATION_BYTES = 8 * 1024 * 1024
|
||||
MAX_TARGET_OUTPUT_BYTES = 1024 * 1024
|
||||
OUTPUT_DRAIN_BYTES_PER_PASS = 1024 * 1024
|
||||
OUTPUT_EOF_WAIT_SECONDS = 1.0
|
||||
OUTPUT_READER_JOIN_SECONDS = 0.5
|
||||
LISTENER_NAME = "rea-native-call-observation"
|
||||
|
||||
|
||||
def _can_retain_trace(current_bytes, current_frames, event_bytes, event_frames):
|
||||
"""Whether a complete event fits both aggregate trace budgets."""
|
||||
return (
|
||||
current_bytes + event_bytes <= MAX_TRACE_BYTES
|
||||
and current_frames + event_frames <= MAX_TRACE_FRAMES
|
||||
)
|
||||
|
||||
|
||||
def _estimated_json_size(value):
|
||||
"""Estimate JSON size without encoding potentially oversized values."""
|
||||
if isinstance(value, str):
|
||||
# json.dumps defaults to ensure_ascii=True; supplementary characters
|
||||
# can become two six-byte Unicode escapes.
|
||||
return len(value) * 12 + 2
|
||||
if value is None or isinstance(value, (bool, int, float)):
|
||||
return 32
|
||||
if isinstance(value, dict):
|
||||
return 2 + sum(
|
||||
len(key) * 6 + 4 + _estimated_json_size(item)
|
||||
for key, item in value.items()
|
||||
)
|
||||
if isinstance(value, (list, tuple)):
|
||||
return 2 + sum(_estimated_json_size(item) + 1 for item in value)
|
||||
return 64
|
||||
|
||||
|
||||
def _fits_trace_budget(value, remaining_bytes):
|
||||
return _estimated_json_size(value) <= remaining_bytes
|
||||
|
||||
|
||||
class _ObservationJournal:
|
||||
"""Flush a bounded JSONL prefix so observations survive tracer failure."""
|
||||
|
||||
def __init__(self, handle, max_bytes=MAX_OBSERVATION_JOURNAL_BYTES):
|
||||
self.handle = handle
|
||||
self.max_bytes = max_bytes
|
||||
self.bytes = 0
|
||||
|
||||
def append(self, record):
|
||||
encoded = (json.dumps(record) + "\n").encode("utf-8")
|
||||
if self.bytes + len(encoded) > self.max_bytes:
|
||||
return False
|
||||
self.handle.write(encoded)
|
||||
self.handle.flush()
|
||||
self.bytes += len(encoded)
|
||||
return True
|
||||
|
||||
|
||||
def _set_launch_flags(launch):
|
||||
launch.SetLaunchFlags(
|
||||
launch.GetLaunchFlags()
|
||||
| lldb.eLaunchFlagDebug
|
||||
| lldb.eLaunchFlagStopAtEntry
|
||||
)
|
||||
|
||||
|
||||
def _hash_file(path):
|
||||
digest = hashlib.sha256()
|
||||
with open(path, "rb") as target_file:
|
||||
while True:
|
||||
chunk = target_file.read(1024 * 1024)
|
||||
if not chunk:
|
||||
break
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def _file_identity(path):
|
||||
info = os.stat(path)
|
||||
return str(info.st_dev), str(info.st_ino)
|
||||
|
||||
|
||||
def _module_identity(target):
|
||||
module = target.FindModule(target.GetExecutable())
|
||||
if not module.IsValid():
|
||||
return None, None, None
|
||||
spec = module.GetFileSpec()
|
||||
path = str(spec) if spec.IsValid() else None
|
||||
identity = None
|
||||
if path:
|
||||
try:
|
||||
identity = _file_identity(path)
|
||||
except OSError:
|
||||
pass
|
||||
return path, identity, module.GetUUIDString() or None
|
||||
|
||||
|
||||
def _wait_for_identity_ack(config):
|
||||
"""Hold the target at entry while the host adapter captures its start identity."""
|
||||
path = config.get("identity_ack_path")
|
||||
if not path:
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
with open(path, "r") as handle:
|
||||
return handle.read(32).strip()
|
||||
except FileNotFoundError:
|
||||
time.sleep(0.025)
|
||||
|
||||
|
||||
class _OutputPipe:
|
||||
"""Drain a target FIFO while retaining only its bounded output prefix."""
|
||||
|
||||
def __init__(self, fifo_path, capture_path, max_bytes):
|
||||
os.mkfifo(fifo_path, 0o600)
|
||||
self.path = fifo_path
|
||||
try:
|
||||
self.fd = os.open(fifo_path, os.O_RDONLY | os.O_NONBLOCK)
|
||||
except Exception:
|
||||
os.unlink(fifo_path)
|
||||
raise
|
||||
try:
|
||||
self.capture = open(capture_path, "wb")
|
||||
except Exception:
|
||||
os.close(self.fd)
|
||||
os.unlink(fifo_path)
|
||||
raise
|
||||
self.max_bytes = max_bytes
|
||||
self.bytes = 0
|
||||
self.truncated = False
|
||||
self.closed = False
|
||||
self.stop_reader = threading.Event()
|
||||
self.reader_thread = None
|
||||
self.reader_error = None
|
||||
self.launched = False
|
||||
self.eof = threading.Event()
|
||||
self.complete = False
|
||||
|
||||
def start(self):
|
||||
if self.reader_thread is None:
|
||||
self.reader_thread = threading.Thread(target=self._reader_loop, daemon=True)
|
||||
self.reader_thread.start()
|
||||
|
||||
def _reader_loop(self):
|
||||
try:
|
||||
while not self.stop_reader.is_set():
|
||||
readable, _, _ = select.select([self.fd], [], [], 0.05)
|
||||
if not readable:
|
||||
continue
|
||||
if self.drain(OUTPUT_DRAIN_BYTES_PER_PASS) == 0:
|
||||
# A FIFO read end is readable at EOF before the target opens it.
|
||||
time.sleep(0.01)
|
||||
except Exception as error:
|
||||
self.reader_error = error
|
||||
self.stop_reader.set()
|
||||
|
||||
def mark_launched(self):
|
||||
self.launched = True
|
||||
|
||||
def drain(self, max_bytes=OUTPUT_DRAIN_BYTES_PER_PASS):
|
||||
consumed = 0
|
||||
while consumed < max_bytes:
|
||||
try:
|
||||
chunk = os.read(self.fd, min(65_536, max_bytes - consumed))
|
||||
except OSError as error:
|
||||
if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
|
||||
return consumed
|
||||
raise
|
||||
if not chunk:
|
||||
if self.launched:
|
||||
self.eof.set()
|
||||
return consumed
|
||||
consumed += len(chunk)
|
||||
self.bytes += len(chunk)
|
||||
remaining = max(0, self.max_bytes - self.capture.tell())
|
||||
if remaining:
|
||||
self.capture.write(chunk[:remaining])
|
||||
if len(chunk) > remaining:
|
||||
self.truncated = True
|
||||
|
||||
def close(self, expected_exit=False):
|
||||
if self.closed:
|
||||
return
|
||||
if expected_exit and self.launched:
|
||||
self.eof.wait(OUTPUT_EOF_WAIT_SECONDS)
|
||||
self.stop_reader.set()
|
||||
if self.reader_thread is not None:
|
||||
self.reader_thread.join(OUTPUT_READER_JOIN_SECONDS)
|
||||
stopped = self.reader_thread is None or not self.reader_thread.is_alive()
|
||||
if stopped:
|
||||
try:
|
||||
self.drain(OUTPUT_DRAIN_BYTES_PER_PASS)
|
||||
finally:
|
||||
try:
|
||||
self.capture.close()
|
||||
finally:
|
||||
os.close(self.fd)
|
||||
self.closed = True
|
||||
else:
|
||||
# A daemon reader may still be unwinding a blocked filesystem write.
|
||||
# Leave its handles alone rather than racing that thread at shutdown.
|
||||
self.closed = True
|
||||
self.complete = bool(
|
||||
stopped
|
||||
and (not self.launched or (expected_exit and self.eof.is_set()))
|
||||
and self.reader_error is None
|
||||
)
|
||||
if not self.complete:
|
||||
self.truncated = True
|
||||
|
||||
|
||||
def _hex(value):
|
||||
return "0x%x" % value
|
||||
|
||||
|
||||
def _location(address, target):
|
||||
"""Load and file address, module and symbol of one code address."""
|
||||
module = address.GetModule()
|
||||
symbol = address.GetSymbol()
|
||||
file_address = address.GetFileAddress()
|
||||
location = {
|
||||
"load_address": _hex(address.GetLoadAddress(target)),
|
||||
"file_address": None
|
||||
if file_address == lldb.LLDB_INVALID_ADDRESS
|
||||
else _hex(file_address),
|
||||
"module": module.GetFileSpec().GetFilename() if module.IsValid() else None,
|
||||
"module_path": str(module.GetFileSpec()) if module.IsValid() else None,
|
||||
"symbol": symbol.GetName() if symbol.IsValid() else None,
|
||||
}
|
||||
return location
|
||||
|
||||
|
||||
def _no_files():
|
||||
return lldb.SBFileSpecList()
|
||||
|
||||
|
||||
def _create_breakpoints(target, spec):
|
||||
"""LLDB breakpoints for one request; a class-qualified method may need two."""
|
||||
if spec["kind"] == "function":
|
||||
modules = lldb.SBFileSpecList()
|
||||
if spec.get("module"):
|
||||
modules.Append(lldb.SBFileSpec(spec["module"], False))
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
spec["name"], lldb.eFunctionNameTypeAuto, modules, _no_files()
|
||||
)
|
||||
]
|
||||
class_name = spec.get("class_name")
|
||||
method_type = spec.get("method_type", "any")
|
||||
if class_name is None:
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
spec["selector"], lldb.eFunctionNameTypeSelector, _no_files(), _no_files()
|
||||
)
|
||||
]
|
||||
prefixes = {"instance": ["-"], "class": ["+"], "any": ["-", "+"]}[method_type]
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
"%s[%s %s]" % (prefix, class_name, spec["selector"]),
|
||||
lldb.eFunctionNameTypeFull,
|
||||
_no_files(),
|
||||
_no_files(),
|
||||
)
|
||||
for prefix in prefixes
|
||||
]
|
||||
|
||||
|
||||
def _dynamic_class(target, pointer):
|
||||
"""Class of an Objective-C receiver from LLDB's runtime reader, without running code."""
|
||||
if pointer == 0:
|
||||
return None
|
||||
data = lldb.SBData.CreateDataFromUInt64Array(
|
||||
target.GetByteOrder(), target.GetAddressByteSize(), [pointer]
|
||||
)
|
||||
value = target.CreateValueFromData(
|
||||
"receiver", data, target.GetBasicType(lldb.eBasicTypeObjCID)
|
||||
)
|
||||
dynamic = value.GetDynamicValue(lldb.eDynamicDontRunTarget)
|
||||
name = dynamic.GetTypeName() if dynamic.IsValid() else None
|
||||
if not name or name in ("id", "objc_object *"):
|
||||
return None
|
||||
return name[:-2] if name.endswith(" *") else name
|
||||
|
||||
|
||||
def _wanted(spec, symbol):
|
||||
"""Selector-only requests may admit only instance or only class methods."""
|
||||
if spec["kind"] != "objc-method" or spec.get("class_name") is not None:
|
||||
return True
|
||||
method_type = spec.get("method_type", "any")
|
||||
if method_type == "any":
|
||||
return True
|
||||
return symbol.startswith("-[" if method_type == "instance" else "+[")
|
||||
|
||||
|
||||
def _read_register(frame, name):
|
||||
error = lldb.SBError()
|
||||
value = frame.FindRegister(name)
|
||||
if not value.IsValid():
|
||||
raise RuntimeError("LLDB breakpoint register %s is unavailable" % name)
|
||||
number = value.GetValueAsUnsigned(error)
|
||||
if error.Fail():
|
||||
raise RuntimeError(
|
||||
"LLDB breakpoint register %s could not be read: %s" % (name, error)
|
||||
)
|
||||
return number
|
||||
|
||||
|
||||
def _record(context, thread, frame, index, sequence, location, remaining_bytes):
|
||||
target, process, config = context["target"], context["process"], context["config"]
|
||||
symbol = location["symbol"] or ""
|
||||
if not _wanted(config["breakpoints"][index], symbol):
|
||||
return None
|
||||
names = ARGUMENT_REGISTERS.get(config["architecture"], [])
|
||||
objc_method = symbol.startswith("-[") or symbol.startswith("+[")
|
||||
values = {
|
||||
name: _read_register(frame, name)
|
||||
for name in names[: max(config["argument_registers"], 2 if objc_method else 0)]
|
||||
}
|
||||
registers = [
|
||||
{"name": name, "value": _hex(values[name])}
|
||||
for name in names[: config["argument_registers"]]
|
||||
]
|
||||
receiver_class = None
|
||||
selector = None
|
||||
if objc_method:
|
||||
error = lldb.SBError()
|
||||
text = process.ReadCStringFromMemory(
|
||||
values[names[1]],
|
||||
MAX_SELECTOR_BYTES,
|
||||
error,
|
||||
)
|
||||
selector = text if error.Success() and text else None
|
||||
if symbol.startswith("-["):
|
||||
receiver_class = _dynamic_class(target, values[names[0]])
|
||||
estimated_base = (
|
||||
_estimated_json_size(location)
|
||||
+ _estimated_json_size(receiver_class)
|
||||
+ _estimated_json_size(selector)
|
||||
+ 2048
|
||||
)
|
||||
if estimated_base > remaining_bytes:
|
||||
raise ValueError("trace event exceeds aggregate byte budget")
|
||||
record = {
|
||||
"sequence": sequence,
|
||||
"elapsed_ms": round((time.monotonic() - context["started"]) * 1000, 3),
|
||||
"thread_id": thread.GetThreadID(),
|
||||
"breakpoint_index": index,
|
||||
"receiver_class": receiver_class,
|
||||
"selector": selector,
|
||||
"registers": registers,
|
||||
"backtrace": [],
|
||||
}
|
||||
record.update(location)
|
||||
return record
|
||||
|
||||
|
||||
# One trace runs in each owned LLDB subprocess. Its callbacks execute at the
|
||||
# breakpoint before LLDB resumes, unlike snapshots of later public stop events.
|
||||
_TRACE_CONTEXT = None
|
||||
|
||||
|
||||
def _handle_breakpoint(context, frame, index):
|
||||
"""Read one hit while LLDB's breakpoint callback owns the stopped frame."""
|
||||
if not frame.IsValid() or frame.GetPC() == lldb.LLDB_INVALID_ADDRESS:
|
||||
raise RuntimeError("LLDB breakpoint callback did not supply a readable frame")
|
||||
thread = frame.GetThread()
|
||||
events = context["events"]
|
||||
location = _location(frame.GetPCAddress(), context["target"])
|
||||
symbol = location["symbol"] or ""
|
||||
if not _wanted(context["config"]["breakpoints"][index], symbol):
|
||||
return None
|
||||
frame_count = max(
|
||||
0,
|
||||
min(thread.GetNumFrames() - 1, context["config"]["backtrace_frames"]),
|
||||
)
|
||||
event_frame_count = 1 + frame_count
|
||||
if not _can_retain_trace(
|
||||
context["retained_trace_bytes"],
|
||||
context["retained_frames"],
|
||||
0,
|
||||
event_frame_count,
|
||||
):
|
||||
return "resource-limit"
|
||||
try:
|
||||
remaining_bytes = MAX_TRACE_BYTES - context["retained_trace_bytes"]
|
||||
record = _record(
|
||||
context, thread, frame, index, len(events), location, remaining_bytes
|
||||
)
|
||||
except ValueError:
|
||||
return "resource-limit"
|
||||
if record is not None:
|
||||
if not _fits_trace_budget(record, remaining_bytes):
|
||||
return "resource-limit"
|
||||
record_bytes = len(json.dumps(record).encode("utf-8"))
|
||||
if context["retained_trace_bytes"] + record_bytes > MAX_TRACE_BYTES:
|
||||
return "resource-limit"
|
||||
for depth in range(1, frame_count + 1):
|
||||
try:
|
||||
location = _location(
|
||||
thread.GetFrameAtIndex(depth).GetPCAddress(), context["target"]
|
||||
)
|
||||
except ValueError:
|
||||
return "resource-limit"
|
||||
if (
|
||||
_estimated_json_size(location) + record_bytes + 1
|
||||
> MAX_TRACE_BYTES - context["retained_trace_bytes"]
|
||||
):
|
||||
return "resource-limit"
|
||||
location_bytes = len(json.dumps(location).encode("utf-8"))
|
||||
addition = location_bytes + (1 if record["backtrace"] else 0)
|
||||
if (
|
||||
context["retained_trace_bytes"]
|
||||
+ record_bytes
|
||||
+ addition
|
||||
> MAX_TRACE_BYTES
|
||||
):
|
||||
return "resource-limit"
|
||||
record["backtrace"].append(location)
|
||||
record_bytes += addition
|
||||
journal = context.get("observation_journal")
|
||||
if journal is not None and not journal.append(
|
||||
{"kind": "event", "event": record}
|
||||
):
|
||||
return "resource-limit"
|
||||
events.append(record)
|
||||
context["retained_frames"] += event_frame_count
|
||||
context["retained_trace_bytes"] += record_bytes
|
||||
if len(events) >= context["config"]["max_events"]:
|
||||
return "event-limit"
|
||||
return None
|
||||
|
||||
|
||||
def breakpoint_hit(frame, location, internal_dict):
|
||||
"""LLDB callback: capture before resuming, and stop on a limit or failure."""
|
||||
context = _TRACE_CONTEXT
|
||||
if context is None or context["outcome"] is not None or context["failure"] is not None:
|
||||
return True
|
||||
try:
|
||||
index = context["breakpoint_index"][location.GetBreakpoint().GetID()]
|
||||
context["outcome"] = _handle_breakpoint(context, frame, index)
|
||||
except Exception as error:
|
||||
context["failure"] = "%s: %s" % (type(error).__name__, error)
|
||||
return True
|
||||
return context["outcome"] is not None
|
||||
|
||||
|
||||
def _handle_stop(context, other_stops):
|
||||
"""Retain non-breakpoint stop observations; callbacks already capture hits."""
|
||||
for thread in context["process"]:
|
||||
if thread.GetStopReason() in (lldb.eStopReasonSignal, lldb.eStopReasonException):
|
||||
stop_description = thread.GetStopDescription(256)
|
||||
journal = context.get("observation_journal")
|
||||
if journal is not None and not journal.append(
|
||||
{"kind": "other-stop", "reason": stop_description}
|
||||
):
|
||||
return "resource-limit"
|
||||
other_stops.append(stop_description)
|
||||
if len(other_stops) >= MAX_OTHER_STOPS:
|
||||
return "stop-limit"
|
||||
return None
|
||||
|
||||
|
||||
def _breakpoint_report(target, created, config):
|
||||
report = []
|
||||
retained_location_bytes = 0
|
||||
locations_truncated = False
|
||||
for index, breakpoints in enumerate(created):
|
||||
locations = []
|
||||
count = 0
|
||||
for breakpoint in breakpoints:
|
||||
for position in range(breakpoint.GetNumLocations()):
|
||||
address = breakpoint.GetLocationAtIndex(position).GetAddress()
|
||||
location = _location(address, target)
|
||||
if not _wanted(config["breakpoints"][index], location["symbol"] or ""):
|
||||
continue
|
||||
count += 1
|
||||
if len(locations) < MAX_REPORTED_LOCATIONS:
|
||||
remaining_bytes = (
|
||||
MAX_BREAKPOINT_LOCATION_BYTES - retained_location_bytes
|
||||
)
|
||||
if not _fits_trace_budget(location, remaining_bytes):
|
||||
locations_truncated = True
|
||||
continue
|
||||
location_bytes = len(json.dumps(location).encode("utf-8"))
|
||||
addition = location_bytes + (1 if locations else 0)
|
||||
if addition > remaining_bytes:
|
||||
locations_truncated = True
|
||||
continue
|
||||
locations.append(location)
|
||||
retained_location_bytes += addition
|
||||
report.append({"index": index, "location_count": count, "locations": locations})
|
||||
return report, locations_truncated
|
||||
|
||||
|
||||
def _launch_info(target, config, listener):
|
||||
launch = lldb.SBLaunchInfo(config["arguments"])
|
||||
launch.SetListener(listener)
|
||||
_set_launch_flags(launch)
|
||||
environment = target.GetEnvironment()
|
||||
for key, value in config["environment"].items():
|
||||
environment.Set(key, value, True)
|
||||
launch.SetEnvironment(environment, False)
|
||||
if config.get("working_directory"):
|
||||
launch.SetWorkingDirectory(config["working_directory"])
|
||||
launch.AddOpenFileAction(0, "/dev/null", True, False)
|
||||
launch.AddOpenFileAction(1, config["stdout_path"], False, True)
|
||||
launch.AddOpenFileAction(2, config["stderr_path"], False, True)
|
||||
return launch
|
||||
|
||||
|
||||
def _wait_for_exit(listener, process, seconds):
|
||||
event = lldb.SBEvent()
|
||||
deadline = time.monotonic() + seconds
|
||||
while process.GetState() != lldb.eStateExited and time.monotonic() < deadline:
|
||||
listener.WaitForEvent(1, event)
|
||||
|
||||
|
||||
def _failed_launch_description(process):
|
||||
if process.GetState() != lldb.eStateExited or process.GetExitStatus() != -1:
|
||||
return None
|
||||
description = process.GetExitDescription()
|
||||
if isinstance(description, str) and description.lower().startswith(("attach failed", "launch failed")):
|
||||
return description
|
||||
return None
|
||||
|
||||
|
||||
def trace(debugger, config):
|
||||
global _TRACE_CONTEXT
|
||||
debugger.SetAsync(True)
|
||||
# Stop at the symbol itself, where argument registers still hold the ABI
|
||||
# arguments, even when line tables would let LLDB skip the prologue.
|
||||
lldb.SBDebugger.SetInternalVariable(
|
||||
"target.skip-prologue", "false", debugger.GetInstanceName()
|
||||
)
|
||||
try:
|
||||
before_digest = _hash_file(config["executable"])
|
||||
before_identity = _file_identity(config["executable"])
|
||||
except OSError as error:
|
||||
return {"status": "target-integrity-error", "error": str(error)}
|
||||
if before_digest != config["expected_sha256"]:
|
||||
return {
|
||||
"status": "target-integrity-error",
|
||||
"error": "selected executable digest changed before LLDB target creation",
|
||||
}
|
||||
error = lldb.SBError()
|
||||
target = debugger.CreateTarget(
|
||||
config["executable"], config["architecture"], None, False, error
|
||||
)
|
||||
if not target.IsValid():
|
||||
return {"status": "target-error", "error": error.GetCString()}
|
||||
module_path_before, module_file_identity_before, module_uuid_before = (
|
||||
_module_identity(target)
|
||||
)
|
||||
if (
|
||||
module_file_identity_before is not None
|
||||
and module_file_identity_before != before_identity
|
||||
):
|
||||
return {
|
||||
"status": "target-integrity-error",
|
||||
"error": "LLDB executable module resolved to a different pre-launch file identity",
|
||||
}
|
||||
created = [_create_breakpoints(target, spec) for spec in config["breakpoints"]]
|
||||
breakpoint_index = {}
|
||||
for index, breakpoints in enumerate(created):
|
||||
for breakpoint in breakpoints:
|
||||
breakpoint_index[breakpoint.GetID()] = index
|
||||
listener = lldb.SBListener(LISTENER_NAME)
|
||||
output_budget = config.get("max_output_bytes", MAX_TARGET_OUTPUT_BYTES)
|
||||
stdout = _OutputPipe(
|
||||
config["stdout_path"], config["stdout_capture_path"], output_budget
|
||||
)
|
||||
try:
|
||||
stderr = _OutputPipe(
|
||||
config["stderr_path"], config["stderr_capture_path"], output_budget
|
||||
)
|
||||
except Exception:
|
||||
stdout.close()
|
||||
raise
|
||||
stdout.start()
|
||||
stderr.start()
|
||||
observation_journal = None
|
||||
started = time.monotonic()
|
||||
process = None
|
||||
try:
|
||||
observation_path = config.get("observation_path")
|
||||
if observation_path:
|
||||
observation_journal = _ObservationJournal(
|
||||
open(observation_path, "wb")
|
||||
)
|
||||
process = target.Launch(_launch_info(target, config, listener), error)
|
||||
if not error.Success() or not process.IsValid():
|
||||
stdout.close()
|
||||
stderr.close()
|
||||
return {"status": "launch-error", "error": error.GetCString()}
|
||||
launch_failure = _failed_launch_description(process)
|
||||
if launch_failure is not None:
|
||||
stdout.close(expected_exit=True)
|
||||
stderr.close(expected_exit=True)
|
||||
return {"status": "launch-error", "error": launch_failure}
|
||||
stdout.mark_launched()
|
||||
stderr.mark_launched()
|
||||
# Publish the PID before any post-launch file or module inspection. The
|
||||
# process is stopped at entry and remains there until identity_ack.
|
||||
with open(config["pid_path"], "w") as handle:
|
||||
handle.write(str(process.GetProcessID()))
|
||||
identity_ack = _wait_for_identity_ack(config)
|
||||
if identity_ack != "readable":
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
expected_exit = process.GetState() == lldb.eStateExited
|
||||
stdout.close(expected_exit=expected_exit)
|
||||
stderr.close(expected_exit=expected_exit)
|
||||
return {
|
||||
"status": "target-identity-unavailable",
|
||||
"error": "host could not establish the launched process identity; target was stopped",
|
||||
}
|
||||
try:
|
||||
after_digest = _hash_file(config["executable"])
|
||||
after_identity = _file_identity(config["executable"])
|
||||
except OSError as identity_error:
|
||||
process.Kill()
|
||||
stdout.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
stderr.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
return {"status": "target-integrity-error", "error": str(identity_error)}
|
||||
if after_digest != config["expected_sha256"] or after_identity != before_identity:
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
stdout.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
stderr.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
return {
|
||||
"status": "target-integrity-error",
|
||||
"error": "selected executable digest or file identity changed during LLDB launch",
|
||||
}
|
||||
module_path_after, module_file_identity_after, module_uuid_after = (
|
||||
_module_identity(target)
|
||||
)
|
||||
if (
|
||||
module_file_identity_after is not None
|
||||
and module_file_identity_after != before_identity
|
||||
):
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
stdout.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
stderr.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
return {
|
||||
"status": "target-integrity-error",
|
||||
"error": "LLDB executable module resolved to a different file identity",
|
||||
}
|
||||
if (
|
||||
module_uuid_before is not None
|
||||
and module_uuid_after is not None
|
||||
and module_uuid_before != module_uuid_after
|
||||
):
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
stdout.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
stderr.close(expected_exit=process.GetState() == lldb.eStateExited)
|
||||
return {
|
||||
"status": "target-integrity-error",
|
||||
"error": "LLDB executable module identity changed during launch",
|
||||
}
|
||||
context = {
|
||||
"target": target,
|
||||
"process": process,
|
||||
"config": config,
|
||||
"started": started,
|
||||
"breakpoint_index": breakpoint_index,
|
||||
"retained_frames": 0,
|
||||
"retained_trace_bytes": 0,
|
||||
"observation_journal": observation_journal,
|
||||
}
|
||||
events = []
|
||||
context.update({"events": events, "outcome": None, "failure": None})
|
||||
_TRACE_CONTEXT = context
|
||||
for breakpoints in created:
|
||||
for breakpoint in breakpoints:
|
||||
breakpoint.SetScriptCallbackFunction("rea_lldb_tracer.breakpoint_hit")
|
||||
other_stops = []
|
||||
outcome = None
|
||||
deadline = started + config["duration_ms"] / 1000
|
||||
event = lldb.SBEvent()
|
||||
while outcome is None:
|
||||
if stdout.reader_error is not None:
|
||||
raise RuntimeError("stdout capture failed: %s" % stdout.reader_error)
|
||||
if stderr.reader_error is not None:
|
||||
raise RuntimeError("stderr capture failed: %s" % stderr.reader_error)
|
||||
if time.monotonic() >= deadline:
|
||||
outcome = "duration-elapsed"
|
||||
break
|
||||
if not listener.WaitForEvent(1, event):
|
||||
continue
|
||||
if not lldb.SBProcess.EventIsProcessEvent(event):
|
||||
continue
|
||||
state = lldb.SBProcess.GetStateFromEvent(event)
|
||||
if state == lldb.eStateExited:
|
||||
outcome = "exited"
|
||||
elif state == lldb.eStateStopped and not lldb.SBProcess.GetRestartedFromEvent(event):
|
||||
if context["failure"] is not None:
|
||||
raise RuntimeError(context["failure"])
|
||||
outcome = context["outcome"] or _handle_stop(context, other_stops)
|
||||
if outcome is None:
|
||||
process.Continue()
|
||||
pid = process.GetProcessID()
|
||||
exit_status = None
|
||||
exit_description = None
|
||||
if outcome == "exited":
|
||||
exit_status = process.GetExitStatus()
|
||||
exit_description = process.GetExitDescription()
|
||||
else:
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
process_exited = process.GetState() == lldb.eStateExited
|
||||
stdout.close(expected_exit=process_exited)
|
||||
stderr.close(expected_exit=process_exited)
|
||||
# SBTarget.Launch may succeed before debugserver reports an asynchronous
|
||||
# attach failure as an exited SBProcess. Its -1 status is not a target
|
||||
# exit code; preserve the debugger's concrete launch denial.
|
||||
launch_failure = _failed_launch_description(process)
|
||||
if launch_failure is not None:
|
||||
return {"status": "launch-error", "error": launch_failure}
|
||||
launch_identity_stable = (
|
||||
module_path_before is not None
|
||||
and module_path_after == module_path_before
|
||||
and module_file_identity_before == before_identity
|
||||
and module_file_identity_after == module_file_identity_before
|
||||
and module_uuid_before is not None
|
||||
and module_uuid_after == module_uuid_before
|
||||
)
|
||||
breakpoint_report, breakpoint_locations_truncated = _breakpoint_report(
|
||||
target, created, config
|
||||
)
|
||||
return {
|
||||
"status": "traced",
|
||||
"version": lldb.SBDebugger.GetVersionString().splitlines()[0],
|
||||
"pid": pid,
|
||||
"outcome": outcome,
|
||||
"exit_status": exit_status,
|
||||
"exit_description": exit_description,
|
||||
"killed": outcome != "exited",
|
||||
"terminated": process.GetState() == lldb.eStateExited,
|
||||
"elapsed_ms": round((time.monotonic() - started) * 1000, 3),
|
||||
"breakpoints": breakpoint_report,
|
||||
"breakpoint_locations_truncated": breakpoint_locations_truncated,
|
||||
"events": events,
|
||||
"resource_limit_reached": outcome == "resource-limit",
|
||||
"target_output": {
|
||||
"stdout_bytes": stdout.bytes,
|
||||
"stderr_bytes": stderr.bytes,
|
||||
"stdout_truncated": stdout.truncated,
|
||||
"stderr_truncated": stderr.truncated,
|
||||
"stdout_complete": stdout.complete,
|
||||
"stderr_complete": stderr.complete,
|
||||
},
|
||||
"target_identity": {
|
||||
"selected_file_sha256": after_digest,
|
||||
"loaded_image_sha256": None,
|
||||
"file_device": after_identity[0],
|
||||
"file_inode": after_identity[1],
|
||||
"module_path": module_path_after,
|
||||
"module_uuid": module_uuid_after,
|
||||
# This records matching path/module identity observations. LLDB
|
||||
# does not expose the exact mapped bytes, so it never proves a digest.
|
||||
"stable": launch_identity_stable,
|
||||
},
|
||||
"other_stops": other_stops,
|
||||
}
|
||||
|
||||
finally:
|
||||
_TRACE_CONTEXT = None
|
||||
if observation_journal is not None:
|
||||
try:
|
||||
observation_journal.handle.close()
|
||||
except Exception:
|
||||
pass
|
||||
if process is not None:
|
||||
try:
|
||||
if process.IsValid() and process.GetState() != lldb.eStateExited:
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
except Exception:
|
||||
pass
|
||||
for output_pipe in (stdout, stderr):
|
||||
try:
|
||||
output_pipe.close(
|
||||
expected_exit=(
|
||||
process is not None
|
||||
and process.IsValid()
|
||||
and process.GetState() == lldb.eStateExited
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def run(debugger, command, result, internal_dict):
|
||||
"""LLDB command entry point: `rea_trace <config.json>`."""
|
||||
with open(command.strip()) as handle:
|
||||
config = json.load(handle)
|
||||
try:
|
||||
output = trace(debugger, config)
|
||||
except Exception as error: # Reported as a tracer failure, never re-raised into LLDB.
|
||||
output = {"status": "tracer-error", "error": "%s: %s" % (type(error).__name__, error)}
|
||||
with open(config["result_path"], "w") as handle:
|
||||
json.dump(output, handle)
|
||||
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand("command script add -f rea_lldb_tracer.run rea_trace")
|
||||
@@ -0,0 +1,273 @@
|
||||
"""Source-owned tests for the LLDB bridge's bounded retention helpers."""
|
||||
|
||||
from io import BytesIO
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
import unittest
|
||||
|
||||
|
||||
fake_lldb = sys.modules.setdefault("lldb", types.ModuleType("lldb"))
|
||||
fake_lldb.LLDB_INVALID_ADDRESS = -1
|
||||
fake_lldb.eLaunchFlagDebug = 2
|
||||
fake_lldb.eLaunchFlagStopAtEntry = 4
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"rea_lldb_tracer", os.path.join(os.path.dirname(__file__), "rea_lldb_tracer.py")
|
||||
)
|
||||
tracer = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(tracer)
|
||||
|
||||
|
||||
class NativeTraceBudgetTests(unittest.TestCase):
|
||||
def test_journal_checks_exact_byte_budget_before_writing(self):
|
||||
record = {"kind": "other-stop", "reason": "signal"}
|
||||
encoded_size = len((tracer.json.dumps(record) + "\n").encode("utf-8"))
|
||||
stream = BytesIO()
|
||||
journal = tracer._ObservationJournal(stream, max_bytes=encoded_size - 1)
|
||||
self.assertFalse(journal.append(record))
|
||||
self.assertEqual(stream.getvalue(), b"")
|
||||
self.assertEqual(journal.bytes, 0)
|
||||
|
||||
def test_journal_appends_each_row_once_and_flushes_it(self):
|
||||
class CountingStream(BytesIO):
|
||||
writes = 0
|
||||
flushes = 0
|
||||
|
||||
def write(self, value):
|
||||
self.writes += 1
|
||||
return super().write(value)
|
||||
|
||||
def flush(self):
|
||||
self.flushes += 1
|
||||
return super().flush()
|
||||
|
||||
stream = CountingStream()
|
||||
journal = tracer._ObservationJournal(stream)
|
||||
journal.append({"kind": "event", "event": {"sequence": 0}})
|
||||
journal.append({"kind": "other-stop", "reason": "signal"})
|
||||
self.assertEqual((stream.writes, stream.flushes), (2, 2))
|
||||
self.assertEqual(journal.bytes, len(stream.getvalue()))
|
||||
|
||||
def test_aggregate_trace_budget_admits_exact_boundary_and_rejects_next_record(self):
|
||||
self.assertTrue(
|
||||
tracer._can_retain_trace(
|
||||
tracer.MAX_TRACE_BYTES - 10,
|
||||
tracer.MAX_TRACE_FRAMES - 2,
|
||||
10,
|
||||
2,
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
tracer._can_retain_trace(
|
||||
tracer.MAX_TRACE_BYTES - 10,
|
||||
tracer.MAX_TRACE_FRAMES - 2,
|
||||
11,
|
||||
2,
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
tracer._can_retain_trace(
|
||||
tracer.MAX_TRACE_BYTES - 10,
|
||||
tracer.MAX_TRACE_FRAMES - 2,
|
||||
10,
|
||||
3,
|
||||
)
|
||||
)
|
||||
|
||||
def test_oversized_producer_string_is_rejected_before_serialization(self):
|
||||
self.assertTrue(tracer._fits_trace_budget("method", 1024))
|
||||
self.assertFalse(tracer._fits_trace_budget("m" * 2048, 1024))
|
||||
self.assertGreater(
|
||||
tracer._estimated_json_size("🙂"), len('"🙂"'.encode("utf-8"))
|
||||
)
|
||||
|
||||
def test_launch_flags_preserve_default_debug_and_stop_at_entry(self):
|
||||
class Launch:
|
||||
flags = 10
|
||||
|
||||
def GetLaunchFlags(self):
|
||||
return self.flags
|
||||
|
||||
def SetLaunchFlags(self, flags):
|
||||
self.flags = flags
|
||||
|
||||
launch = Launch()
|
||||
tracer._set_launch_flags(launch)
|
||||
self.assertEqual(launch.flags, 14)
|
||||
|
||||
def test_output_pipe_counts_all_bytes_and_keeps_only_the_prefix(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
fifo = os.path.join(directory, "stdout.fifo")
|
||||
capture = os.path.join(directory, "stdout.capture")
|
||||
sink = tracer._OutputPipe(fifo, capture, 5)
|
||||
sink.start()
|
||||
sink.mark_launched()
|
||||
writer = os.open(fifo, os.O_WRONLY)
|
||||
try:
|
||||
for index in range(100):
|
||||
chunk = (b"hello world" if index == 0 else b"x" * 4096)
|
||||
os.write(writer, chunk)
|
||||
finally:
|
||||
os.close(writer)
|
||||
sink.drain()
|
||||
sink.close(expected_exit=True)
|
||||
with open(capture, "rb") as retained:
|
||||
self.assertEqual(retained.read(), b"hello")
|
||||
self.assertEqual(sink.bytes, 11 + 99 * 4096)
|
||||
self.assertTrue(sink.truncated)
|
||||
self.assertTrue(sink.complete)
|
||||
|
||||
def test_output_pipe_reports_incomplete_capture_after_unexpected_close(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
fifo = os.path.join(directory, "stdout.fifo")
|
||||
capture = os.path.join(directory, "stdout.capture")
|
||||
sink = tracer._OutputPipe(fifo, capture, 5)
|
||||
sink.start()
|
||||
sink.mark_launched()
|
||||
sink.close(expected_exit=False)
|
||||
self.assertFalse(sink.complete)
|
||||
self.assertTrue(sink.truncated)
|
||||
|
||||
def test_selector_location_report_uses_the_requested_method_type(self):
|
||||
class Symbol:
|
||||
def IsValid(self):
|
||||
return True
|
||||
|
||||
def GetName(self):
|
||||
return "+[Greeter shared]"
|
||||
|
||||
class FileSpec:
|
||||
def GetFilename(self):
|
||||
return "Fixture"
|
||||
|
||||
def __str__(self):
|
||||
return "/tmp/Fixture"
|
||||
|
||||
class Module:
|
||||
def IsValid(self):
|
||||
return True
|
||||
|
||||
def GetFileSpec(self):
|
||||
return FileSpec()
|
||||
|
||||
class Address:
|
||||
def GetModule(self):
|
||||
return Module()
|
||||
|
||||
def GetSymbol(self):
|
||||
return Symbol()
|
||||
|
||||
def GetFileAddress(self):
|
||||
return 1
|
||||
|
||||
def GetLoadAddress(self, target):
|
||||
return 2
|
||||
|
||||
class Location:
|
||||
def GetAddress(self):
|
||||
return Address()
|
||||
|
||||
class Breakpoint:
|
||||
def GetNumLocations(self):
|
||||
return 1
|
||||
|
||||
def GetLocationAtIndex(self, index):
|
||||
return Location()
|
||||
|
||||
report, locations_truncated = tracer._breakpoint_report(
|
||||
object(),
|
||||
[[Breakpoint()]],
|
||||
{
|
||||
"breakpoints": [
|
||||
{
|
||||
"kind": "objc-method",
|
||||
"selector": "shared",
|
||||
"method_type": "instance",
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
self.assertEqual(report[0]["location_count"], 0)
|
||||
self.assertEqual(report[0]["locations"], [])
|
||||
self.assertFalse(locations_truncated)
|
||||
|
||||
def test_breakpoint_report_bounds_aggregate_location_json(self):
|
||||
tracer.lldb.LLDB_INVALID_ADDRESS = -1
|
||||
symbol_name = (
|
||||
"-[VeryLongButPlausibleObjectiveCController "
|
||||
"performTransactionWithRequestIdentifier:accountContext:"
|
||||
"securityPolicy:completionHandler:]"
|
||||
)
|
||||
module_path = (
|
||||
"/Applications/Enterprise Product.app/Contents/Frameworks/"
|
||||
"EnterpriseSubsystem.framework/Versions/A/EnterpriseSubsystem"
|
||||
)
|
||||
|
||||
class Symbol:
|
||||
def IsValid(self):
|
||||
return True
|
||||
|
||||
def GetName(self):
|
||||
return symbol_name
|
||||
|
||||
class FileSpec:
|
||||
def GetFilename(self):
|
||||
return "EnterpriseSubsystem"
|
||||
|
||||
def __str__(self):
|
||||
return module_path
|
||||
|
||||
class Module:
|
||||
def IsValid(self):
|
||||
return True
|
||||
|
||||
def GetFileSpec(self):
|
||||
return FileSpec()
|
||||
|
||||
class Address:
|
||||
def GetModule(self):
|
||||
return Module()
|
||||
|
||||
def GetSymbol(self):
|
||||
return Symbol()
|
||||
|
||||
def GetFileAddress(self):
|
||||
return 0x123456789
|
||||
|
||||
def GetLoadAddress(self, target):
|
||||
return 0x7123456789
|
||||
|
||||
class Location:
|
||||
def GetAddress(self):
|
||||
return Address()
|
||||
|
||||
class Breakpoint:
|
||||
def GetNumLocations(self):
|
||||
return 64
|
||||
|
||||
def GetLocationAtIndex(self, index):
|
||||
return Location()
|
||||
|
||||
breakpoint_count = 1000
|
||||
created = [[Breakpoint()] for _ in range(breakpoint_count)]
|
||||
config = {"breakpoints": [{"kind": "objc-method"}] * breakpoint_count}
|
||||
report, locations_truncated = tracer._breakpoint_report(
|
||||
object(), created, config
|
||||
)
|
||||
retained_bytes = sum(
|
||||
len(json.dumps(location).encode("utf-8"))
|
||||
+ (1 if index > 0 else 0)
|
||||
for row in report
|
||||
for index, location in enumerate(row["locations"])
|
||||
)
|
||||
self.assertTrue(locations_truncated)
|
||||
self.assertEqual(sum(row["location_count"] for row in report), 64_000)
|
||||
self.assertLess(sum(len(row["locations"]) for row in report), 64_000)
|
||||
self.assertLessEqual(retained_bytes, tracer.MAX_BREAKPOINT_LOCATION_BYTES)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,297 @@
|
||||
import Darwin
|
||||
import Foundation
|
||||
|
||||
struct RunTokenObservation: Encodable {
|
||||
let pid: Int32
|
||||
let state: String
|
||||
let run_id: String?
|
||||
let reason: String?
|
||||
}
|
||||
|
||||
struct ProcessIdentityObservation: Encodable {
|
||||
let pid: Int32
|
||||
let state: String
|
||||
let identity: String?
|
||||
let reason: String?
|
||||
}
|
||||
|
||||
private struct ProcessMetadata {
|
||||
let identity: String
|
||||
let pointerSize: Int
|
||||
}
|
||||
|
||||
struct ProcessSnapshot {
|
||||
fileprivate let metadata: [Int32: ProcessMetadata]
|
||||
let failure: String?
|
||||
}
|
||||
|
||||
typealias ProcessTableSysctlCall = (
|
||||
_ mib: inout [Int32],
|
||||
_ output: UnsafeMutableRawPointer?,
|
||||
_ size: inout Int
|
||||
) -> Int32
|
||||
|
||||
private func readProcessSnapshot() -> ProcessSnapshot {
|
||||
readProcessSnapshot { mib, output, size in
|
||||
sysctl(&mib, u_int(mib.count), output, &size, nil, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func readProcessSnapshot(
|
||||
sysctlCall: ProcessTableSysctlCall
|
||||
) -> ProcessSnapshot {
|
||||
for _ in 0..<3 {
|
||||
var mib = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]
|
||||
var size = 0
|
||||
guard sysctlCall(&mib, nil, &size) == 0 else {
|
||||
let failure = errno
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
|
||||
}
|
||||
guard size >= MemoryLayout<kinfo_proc>.size,
|
||||
size % MemoryLayout<kinfo_proc>.stride == 0 else {
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
|
||||
}
|
||||
var processes = [kinfo_proc](
|
||||
repeating: kinfo_proc(),
|
||||
count: size / MemoryLayout<kinfo_proc>.stride
|
||||
)
|
||||
let result = processes.withUnsafeMutableBufferPointer { buffer in
|
||||
let output = buffer.baseAddress.map { UnsafeMutableRawPointer($0) }
|
||||
return sysctlCall(&mib, output, &size)
|
||||
}
|
||||
guard result == 0 else {
|
||||
let failure = errno
|
||||
if failure == ENOMEM { continue }
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
|
||||
}
|
||||
guard size % MemoryLayout<kinfo_proc>.stride == 0 else {
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
|
||||
}
|
||||
let processCount = size / MemoryLayout<kinfo_proc>.stride
|
||||
let metadata = Dictionary(
|
||||
processes.prefix(processCount).map { process in
|
||||
let start = process.kp_proc.p_starttime
|
||||
return (
|
||||
process.kp_proc.p_pid,
|
||||
ProcessMetadata(
|
||||
identity: "\(start.tv_sec):\(start.tv_usec)",
|
||||
pointerSize: process.kp_proc.p_flag & P_LP64 != 0 ? 8 : 4
|
||||
)
|
||||
)
|
||||
},
|
||||
uniquingKeysWith: { first, _ in first }
|
||||
)
|
||||
return ProcessSnapshot(metadata: metadata, failure: nil)
|
||||
}
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(ENOMEM)")
|
||||
}
|
||||
|
||||
func readProcessIdentities(pids: [Int32]) -> [ProcessIdentityObservation] {
|
||||
let snapshot = readProcessSnapshot()
|
||||
return pids.map { pid in
|
||||
guard let metadata = snapshot.metadata[pid] else {
|
||||
return unavailableIdentity(
|
||||
pid: pid,
|
||||
reason: snapshot.failure ?? "process_not_in_snapshot"
|
||||
)
|
||||
}
|
||||
return ProcessIdentityObservation(pid: pid, state: "readable", identity: metadata.identity, reason: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func unavailableIdentity(pid: Int32, reason: String) -> ProcessIdentityObservation {
|
||||
ProcessIdentityObservation(pid: pid, state: "unavailable", identity: nil, reason: reason)
|
||||
}
|
||||
|
||||
enum RunTokenReadError: Error {
|
||||
case invalidBuffer
|
||||
case duplicateToken
|
||||
case invalidTokenEncoding
|
||||
case environmentUnavailable
|
||||
case appleVectorUnavailable
|
||||
case ambiguousEnvironmentBoundary
|
||||
}
|
||||
|
||||
@_silgen_name("csops")
|
||||
private func csops(
|
||||
_ pid: Int32,
|
||||
_ ops: UInt32,
|
||||
_ addr: UnsafeMutableRawPointer?,
|
||||
_ size: Int
|
||||
) -> Int32
|
||||
|
||||
private let csOpsStatus: UInt32 = 0
|
||||
private let csPlatformBinary: UInt32 = 0x0400_0000
|
||||
|
||||
/**
|
||||
* Whether the target image is an Apple platform binary. Current macOS omits
|
||||
* the environment of platform binaries from KERN_PROCARGS2 for every caller,
|
||||
* so this distinguishes a policy-withheld environment from a process whose
|
||||
* environment is genuinely absent or transiently unreadable.
|
||||
*/
|
||||
func isPlatformBinary(pid: Int32) -> Bool {
|
||||
var flags: UInt32 = 0
|
||||
let result = withUnsafeMutablePointer(to: &flags) { pointer in
|
||||
csops(pid, csOpsStatus, UnsafeMutableRawPointer(pointer), MemoryLayout<UInt32>.size)
|
||||
}
|
||||
return result == 0 && (flags & csPlatformBinary) != 0
|
||||
}
|
||||
|
||||
private func readRunToken(pid: Int32, pointerSize: Int) -> RunTokenObservation {
|
||||
var mib = [CTL_KERN, KERN_PROCARGS2, Int32(pid)]
|
||||
var size = 0
|
||||
guard sysctl(&mib, u_int(mib.count), nil, &size, nil, 0) == 0, size >= MemoryLayout<Int32>.size else {
|
||||
return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
|
||||
}
|
||||
var bytes = [UInt8](repeating: 0, count: size)
|
||||
let result = bytes.withUnsafeMutableBytes { buffer in
|
||||
sysctl(&mib, u_int(mib.count), buffer.baseAddress, &size, nil, 0)
|
||||
}
|
||||
guard result == 0 else {
|
||||
return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
|
||||
}
|
||||
bytes = Array(bytes.prefix(size))
|
||||
do {
|
||||
let runId = try token(in: bytes, pointerSize: pointerSize)
|
||||
// A complete empty environment is readable for an ordinary image. Apple
|
||||
// platform images can expose the same layout after the kernel withholds
|
||||
// their environment, so they remain explicitly unverified without a token.
|
||||
if runId == nil && isPlatformBinary(pid: pid) {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: runId, reason: nil)
|
||||
} catch RunTokenReadError.duplicateToken {
|
||||
return unavailable(pid: pid, reason: "duplicate_run_token")
|
||||
} catch RunTokenReadError.invalidTokenEncoding {
|
||||
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
|
||||
} catch RunTokenReadError.environmentUnavailable {
|
||||
return unavailable(
|
||||
pid: pid,
|
||||
reason: isPlatformBinary(pid: pid)
|
||||
? "platform_binary_environment_withheld"
|
||||
: "environment_unavailable"
|
||||
)
|
||||
} catch RunTokenReadError.appleVectorUnavailable {
|
||||
return unavailable(pid: pid, reason: "apple_vector_unavailable")
|
||||
} catch RunTokenReadError.ambiguousEnvironmentBoundary {
|
||||
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
|
||||
} catch {
|
||||
return unavailable(pid: pid, reason: "malformed_procargs")
|
||||
}
|
||||
}
|
||||
|
||||
func readRunTokens(pids: [Int32]) -> [RunTokenObservation] {
|
||||
let before = readProcessSnapshot()
|
||||
let observations = pids.map { pid -> RunTokenObservation in
|
||||
guard let metadata = before.metadata[pid] else {
|
||||
return unavailable(pid: pid, reason: before.failure ?? "process_not_in_snapshot")
|
||||
}
|
||||
return readRunToken(pid: pid, pointerSize: metadata.pointerSize)
|
||||
}
|
||||
let after = readProcessSnapshot()
|
||||
return zip(pids, observations).map { pid, observation in
|
||||
guard let initial = before.metadata[pid], let current = after.metadata[pid] else {
|
||||
return unavailable(pid: pid, reason: after.failure ?? "process_not_in_snapshot")
|
||||
}
|
||||
guard initial.identity == current.identity,
|
||||
initial.pointerSize == current.pointerSize else {
|
||||
return unavailable(pid: pid, reason: "process_identity_changed_during_token_read")
|
||||
}
|
||||
return observation
|
||||
}
|
||||
}
|
||||
|
||||
func token(in bytes: [UInt8], pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size) throws -> String? {
|
||||
guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBuffer }
|
||||
let argc = bytes.withUnsafeBytes { $0.loadUnaligned(as: Int32.self).littleEndian }
|
||||
guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBuffer }
|
||||
var offset = MemoryLayout<Int32>.size
|
||||
guard let executablePath = readString(in: bytes, offset: &offset) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
guard pointerSize == 4 || pointerSize == 8 else { throw RunTokenReadError.invalidBuffer }
|
||||
let pathRecordSize = Array("executable_path=".utf8).count + executablePath.count + 1
|
||||
let padding = (pointerSize - pathRecordSize % pointerSize) % pointerSize
|
||||
guard offset + padding <= bytes.count,
|
||||
bytes[offset..<(offset + padding)].allSatisfy({ $0 == 0 }) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
offset += padding
|
||||
for _ in 0..<argc {
|
||||
guard readString(in: bytes, offset: &offset, allowEmpty: true) != nil else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
}
|
||||
guard bytes.last == 0 else { throw RunTokenReadError.invalidBuffer }
|
||||
if offset == bytes.count {
|
||||
throw RunTokenReadError.environmentUnavailable
|
||||
}
|
||||
var trailingEntries: [ArraySlice<UInt8>] = []
|
||||
var zeroedAppleVectorStart: Int?
|
||||
while offset < bytes.count {
|
||||
if bytes[offset] == 0 {
|
||||
let start = offset
|
||||
while offset < bytes.count, bytes[offset] == 0 {
|
||||
offset += 1
|
||||
}
|
||||
if offset - start > 1 {
|
||||
zeroedAppleVectorStart = zeroedAppleVectorStart ?? trailingEntries.count
|
||||
}
|
||||
continue
|
||||
}
|
||||
guard let entry = readString(in: bytes, offset: &offset) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
trailingEntries.append(entry)
|
||||
}
|
||||
// XNU appends its Apple vector after the environment. libc clears the
|
||||
// initial `pfz=` record after startup, leaving a multi-NUL run in its place.
|
||||
// A caller can also set `pfz`, so prefer the final intact sentinel when it
|
||||
// remains available and otherwise stop at the cleared record.
|
||||
let intactAppleVectorStart = trailingEntries.lastIndex(where: {
|
||||
$0.starts(with: Array("pfz=".utf8))
|
||||
})
|
||||
let preferZeroedBoundary = zeroedAppleVectorStart.flatMap { zeroedStart in
|
||||
intactAppleVectorStart.map { intactStart in intactStart < zeroedStart } ?? true
|
||||
} ?? false
|
||||
let appleVectorStart = preferZeroedBoundary
|
||||
? zeroedAppleVectorStart
|
||||
: intactAppleVectorStart
|
||||
guard let appleVectorStart else {
|
||||
throw RunTokenReadError.appleVectorUnavailable
|
||||
}
|
||||
if trailingEntries[appleVectorStart...].contains(where: {
|
||||
$0.starts(with: Array("REA_PROCESS_RUN_ID=".utf8))
|
||||
}) {
|
||||
throw RunTokenReadError.ambiguousEnvironmentBoundary
|
||||
}
|
||||
let environmentEntries = trailingEntries[..<appleVectorStart]
|
||||
var found: String?
|
||||
for entry in environmentEntries {
|
||||
if entry.starts(with: Array("REA_PROCESS_RUN_ID=".utf8)) {
|
||||
guard found == nil else { throw RunTokenReadError.duplicateToken }
|
||||
let value = entry.dropFirst("REA_PROCESS_RUN_ID=".utf8.count)
|
||||
guard let decoded = String(bytes: value, encoding: .utf8) else {
|
||||
throw RunTokenReadError.invalidTokenEncoding
|
||||
}
|
||||
found = decoded
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
private func readString(
|
||||
in bytes: [UInt8],
|
||||
offset: inout Int,
|
||||
allowEmpty: Bool = false
|
||||
) -> ArraySlice<UInt8>? {
|
||||
guard offset < bytes.count, let end = bytes[offset...].firstIndex(of: 0),
|
||||
allowEmpty || end > offset else { return nil }
|
||||
let string = bytes[offset..<end]
|
||||
offset = end + 1
|
||||
return string
|
||||
}
|
||||
|
||||
private func unavailable(pid: Int32, reason: String) -> RunTokenObservation {
|
||||
RunTokenObservation(pid: pid, state: "unavailable", run_id: nil, reason: reason)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
|
||||
struct RunTokenResponse: Encodable {
|
||||
let results: [RunTokenObservation]
|
||||
}
|
||||
|
||||
struct ProcessIdentityResponse: Encodable {
|
||||
let results: [ProcessIdentityObservation]
|
||||
}
|
||||
|
||||
@main
|
||||
struct ProcessRunTokenReaderMain {
|
||||
static func main() {
|
||||
let arguments = Array(CommandLine.arguments.dropFirst())
|
||||
let identityMode = arguments.first == "--identities"
|
||||
let pidArguments = identityMode ? Array(arguments.dropFirst()) : arguments
|
||||
let pids = pidArguments.compactMap(Int32.init)
|
||||
guard pids.count == pidArguments.count else {
|
||||
fputs("invalid process ID input\n", stderr)
|
||||
exit(64)
|
||||
}
|
||||
do {
|
||||
let output: Data
|
||||
if identityMode {
|
||||
output = try JSONEncoder().encode(ProcessIdentityResponse(results: readProcessIdentities(pids: pids)))
|
||||
} else {
|
||||
output = try JSONEncoder().encode(RunTokenResponse(results: readRunTokens(pids: pids)))
|
||||
}
|
||||
FileHandle.standardOutput.write(output)
|
||||
} catch {
|
||||
fputs("could not encode process token observations\n", stderr)
|
||||
exit(70)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
"""Fixed core-only GDB/pwndbg operation; no caller commands or historical PID lookup."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import gdb
|
||||
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
|
||||
class ContextFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def inspect(request):
|
||||
if gdb.selected_inferior().connection is not None:
|
||||
raise ContextFailure("decoder", "Owned GDB unexpectedly has an inferior connection before core loading.")
|
||||
root = Path(os.environ["REA_CORE_CONTEXT_REQUEST"]).parent
|
||||
empty = root / "no-libraries"
|
||||
empty.mkdir(mode=0o700)
|
||||
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off",
|
||||
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries"]:
|
||||
gdb.execute(command, to_string=True)
|
||||
entry = request["gdbinit"]
|
||||
try:
|
||||
exec(compile(Path(entry).read_bytes(), entry, "exec"), {"__name__": "__main__", "__file__": entry})
|
||||
import pwndbg.aglib.vmmap
|
||||
import pwndbg.lib.version
|
||||
except (ImportError, OSError) as error:
|
||||
raise ContextFailure("unavailable", "Selected pwndbg could not initialize: " + str(error)) from error
|
||||
version = pwndbg.lib.version.__version__
|
||||
if version != "2026.09.15" and not version.startswith("2026.09.15 build: "):
|
||||
raise ContextFailure("unsupported", "Expected unchanged pwndbg2026.09.15; selected plugin reports " + version + ".")
|
||||
gdb.execute('set context-sections ""', to_string=True)
|
||||
# core-file takes a raw filename and retains quote characters. Use a fixed
|
||||
# relative filename in the owned cwd so no selected path becomes GDB syntax.
|
||||
if Path(request["snapshot_path"]) != root.parent / "object.snapshot":
|
||||
raise ContextFailure("decoder", "Core context request did not select the shared owned snapshot.")
|
||||
gdb.execute("core-file ../object.snapshot", to_string=True)
|
||||
inferior = gdb.selected_inferior()
|
||||
if inferior.connection is None or inferior.connection.type != "core" or gdb.current_progspace().filename is not None:
|
||||
raise ContextFailure("decoder", "Requested core-only boundary was not established; no mapping query performed.")
|
||||
maps = []
|
||||
for page in pwndbg.aglib.vmmap.get():
|
||||
flags = page.flags
|
||||
maps.append({"start": hex(page.start), "end": hex(page.end), "offset": hex(page.offset), "reported_flags": flags,
|
||||
"permissions": {"read": bool(flags & 4), "write": bool(flags & 2), "execute": bool(flags & 1)} if 1 <= flags <= 7 else None,
|
||||
"pathname_display": page.objfile, "current_file_identity": "unknown"})
|
||||
return {"status": "available", "gdb_version": gdb.VERSION, "pwndbg_version": version, "connection": "core", "executable": None,
|
||||
"historical_pid": inferior.pid, "reported_limits": {"address_space_bytes": resource.getrlimit(resource.RLIMIT_AS)[0], "cpu_seconds": resource.getrlimit(resource.RLIMIT_CPU)[0], "file_size_bytes": resource.getrlimit(resource.RLIMIT_FSIZE)[0]}, "maps": maps, "confidence": "derived", "completeness": "unknown",
|
||||
"diagnostics": {"stdout": "", "stderr": "", "truncated": False}}
|
||||
|
||||
|
||||
def failure_exit(request_path, status, marker):
|
||||
try:
|
||||
descriptor = os.open(Path(request_path).parent / "resource.failure", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
|
||||
def main():
|
||||
request_path = os.environ["REA_CORE_CONTEXT_REQUEST"]
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
reserve = bytearray(1024 * 1024)
|
||||
try:
|
||||
reply = {"ok": True, "value": inspect(request)}
|
||||
except ContextFailure as error:
|
||||
reply = {"ok": False, "reason": error.reason, "message": str(error)}
|
||||
except MemoryError:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
except Exception as error:
|
||||
if isinstance(error, OSError) and error.errno == 12:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
|
||||
try:
|
||||
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
|
||||
if len(encoded) > OUTPUT_BYTES:
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds64 MiB reply budget."}).encode("utf-8")
|
||||
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "wb") as handle:
|
||||
handle.write(encoded)
|
||||
except MemoryError:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
except OSError as error:
|
||||
if error.errno == 27:
|
||||
failure_exit(request_path, 76, b"F")
|
||||
if error.errno == 12:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
raise
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Catch typed resource failures while loading or running the owned debugger launcher.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "launch_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Lower inherited limits before replacing this owned process with GDB."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
runtime_path = Path(__file__).parent.parent / "pwntools" / "decoder_runtime.py"
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
|
||||
|
||||
class LaunchFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def launch(snapshot, cache):
|
||||
request_path = sys.argv[-1]
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
bridge = str(Path(__file__).with_name("core_context.py"))
|
||||
environment = dict(os.environ, REA_CORE_CONTEXT_REQUEST=request_path)
|
||||
# Only fixed package code appears in the GDB Python command. Selected paths stay JSON data.
|
||||
command = "python exec(compile(open(" + repr(bridge) + ", 'rb').read(), " + repr(bridge) + ", 'exec'), {'__name__':'__main__','__file__':" + repr(bridge) + "})"
|
||||
try:
|
||||
os.execve(request["gdb"], [request["gdb"], "-nx", "-nh", "--batch", "--quiet",
|
||||
"-iex", "set auto-load off", "-iex", "set debuginfod enabled off",
|
||||
"-iex", "set auto-solib-add off", "-ex", command], environment)
|
||||
except OSError as error:
|
||||
raise LaunchFailure("unavailable", "Selected GDB could not start (" + str(error.errno) + "): " + request["gdb"] + "; " + str(error)) from error
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime["main"](sys.argv[-1], launch, LaunchFailure, "pwndbg-core-launcher")
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Shared bounded reply/limit protocol, independent of the supplied artifact format."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import sys
|
||||
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
def lower_resource_limits():
|
||||
limits = {}
|
||||
for name, kind, maximum in (
|
||||
("address_space_bytes", resource.RLIMIT_AS, 3 * 1024**3),
|
||||
("cpu_seconds", resource.RLIMIT_CPU, 30),
|
||||
("file_size_bytes", resource.RLIMIT_FSIZE, OUTPUT_BYTES),
|
||||
):
|
||||
soft, hard = resource.getrlimit(kind)
|
||||
finite = [maximum] + [value for value in (soft, hard) if value != resource.RLIM_INFINITY]
|
||||
effective = min(finite)
|
||||
# Preserve the inherited hard boundary and never raise a caller's soft limit.
|
||||
resource.setrlimit(kind, (effective, hard))
|
||||
limits[name] = effective
|
||||
return limits
|
||||
|
||||
|
||||
def main(request_path, inspect, failure_type, profile):
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
os.environ["PWNLIB_NOTERM"] = "1"
|
||||
os.environ["PWNLIB_CACHE_DIR"] = str(Path(request_path).parent / "cache")
|
||||
limits = None
|
||||
# Release bounded emergency headroom before constructing a MemoryError reply.
|
||||
# The outer exit-status contract also covers allocation failure during serialization.
|
||||
memory_reserve = bytearray(1024 * 1024)
|
||||
try:
|
||||
limits = lower_resource_limits()
|
||||
descriptor = os.open(Path(request_path).parent / "limits.json", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
||||
json.dump(limits, handle)
|
||||
value = inspect(Path(request["snapshot_path"]), Path(request_path).parent / "cache")
|
||||
value["limitations"].append("Effective owned Python resource soft limits: " + json.dumps(limits, sort_keys=True) + ". Inherited tighter limits are retained.")
|
||||
reply = {"ok": True, "profile": profile, "value": value}
|
||||
except failure_type as error:
|
||||
reply = {"ok": False, "reason": error.reason, "message": str(error)}
|
||||
except MemoryError:
|
||||
memory_reserve = None
|
||||
reply = {"ok": False, "reason": "resource-limit", "message": "pwntools memory allocation failed under the effective resource limits; the exact allocation cause is unknown.", "reported_limits": limits}
|
||||
except Exception as error:
|
||||
if isinstance(error, OSError) and error.errno == 12:
|
||||
memory_reserve = None
|
||||
reply = {"ok": False, "reason": "resource-limit", "message": "pwntools reported OSError(ENOMEM); the exact allocation cause is unknown.", "reported_limits": limits}
|
||||
else:
|
||||
reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
|
||||
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
|
||||
if len(encoded) > OUTPUT_BYTES:
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete artifact result exceeds the 64 MiB reply budget."}).encode("utf-8")
|
||||
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "wb") as handle:
|
||||
handle.write(encoded)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Catch typed resource failures while loading or running the owned ELF adapter.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "layout_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -0,0 +1,309 @@
|
||||
"""Offline adapter to unchanged pwntools 4.15.0; never use libs/maps/libc.
|
||||
|
||||
Owned request/snapshot/reply paths are supplied by REA. No host target-process launch,
|
||||
user configuration initialization, debugger startup, or dependency installation.
|
||||
"""
|
||||
import base64
|
||||
import importlib.metadata
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import sys
|
||||
|
||||
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
|
||||
PACKAGES = {"pwntools": "4.15.0", "pyelftools": "0.33", "unicorn": "2.1.2"}
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
|
||||
class LayoutFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def address(value):
|
||||
return hex(int(value))
|
||||
|
||||
|
||||
def file_range(offset, size, length):
|
||||
return 0 <= offset <= length and 0 <= size <= length - offset
|
||||
|
||||
|
||||
def location(offset, size, length):
|
||||
if not file_range(offset, size, length):
|
||||
raise LayoutFailure("format", "ELF contains a file-backed range outside the selected snapshot.")
|
||||
return {"offset": address(offset), "bytes": address(size)}
|
||||
|
||||
|
||||
def name_reference(display, section, offset, content):
|
||||
header = getattr(section, "header", None)
|
||||
if header is None or header.sh_type != "SHT_STRTAB":
|
||||
return {"display": display, "bytes_base64": None, "location": None,
|
||||
"unknown_reason": "Referenced string table could not be resolved."}
|
||||
return name_in_table(display, header.sh_offset, header.sh_size, offset, content)
|
||||
|
||||
|
||||
def name_in_table(display, table_start, table_size, offset, content):
|
||||
location(table_start, table_size, len(content))
|
||||
if not 0 <= offset < table_size:
|
||||
raise LayoutFailure("format", f"ELF name offset {offset} lies outside its declared string table of {table_size} bytes at file offset {table_start}.")
|
||||
start = table_start + offset
|
||||
limit = table_start + table_size
|
||||
end = content.find(b"\0", start, limit)
|
||||
if end < 0:
|
||||
raise LayoutFailure("format", "ELF name lacks a terminator within its reported string table.")
|
||||
raw = content[start:end]
|
||||
return {"display": raw.decode("utf-8", "replace") if display is None else display, "bytes_base64": base64.b64encode(raw).decode("ascii"),
|
||||
"location": location(start, len(raw) + 1, len(content)), "unknown_reason": None}
|
||||
|
||||
|
||||
def dynamic_name_reference(tags, offset, image, content):
|
||||
starts = [tag.d_val for tag in tags if tag.d_tag == "DT_STRTAB"]
|
||||
sizes = [tag.d_val for tag in tags if tag.d_tag == "DT_STRSZ"]
|
||||
if not starts or not sizes:
|
||||
raise LayoutFailure("format", "ELF dependency requires DT_STRTAB and DT_STRSZ.")
|
||||
if len(starts) != 1 or len(sizes) != 1:
|
||||
raise LayoutFailure("unsupported-target", "ELF dependency string-table tags are ambiguous: " + repr({"DT_STRTAB": starts, "DT_STRSZ": sizes}))
|
||||
if not 0 <= offset < sizes[0]:
|
||||
raise LayoutFailure("format", f"ELF dependency name offset {offset} lies outside declared DT_STRSZ {sizes[0]}.")
|
||||
offsets = set()
|
||||
if len(starts) == 1 and len(sizes) == 1:
|
||||
for segment in image.iter_segments_by_type("PT_LOAD"):
|
||||
h = segment.header
|
||||
if starts[0] >= h.p_vaddr and starts[0] + sizes[0] <= h.p_vaddr + h.p_filesz:
|
||||
offsets.add(h.p_offset + starts[0] - h.p_vaddr)
|
||||
if len(offsets) != 1:
|
||||
raise LayoutFailure("unsupported-target", "Dynamic string table lacks a unique file-backed mapping: " + repr({"DT_STRTAB": starts, "DT_STRSZ": sizes, "candidate_file_offsets": sorted(offsets)}))
|
||||
return name_in_table(None, offsets.pop(), sizes[0], offset, content)
|
||||
|
||||
|
||||
def symbol_value_meaning(raw, image_type, sections):
|
||||
index = raw.st_shndx
|
||||
if raw.st_info.type == "STT_FILE": return "no-address"
|
||||
if index == "SHN_UNDEF": return "undefined"
|
||||
if index == "SHN_COMMON": return "alignment"
|
||||
if index == "SHN_ABS": return "absolute-value"
|
||||
if not isinstance(index, int) or not 0 < index < len(sections) or index >= 0xff00:
|
||||
return "unknown-section-index"
|
||||
if sections[index].header.sh_type == "SHT_NULL":
|
||||
return "unknown-section-index"
|
||||
if image_type == "ET_REL": return "section-offset"
|
||||
if raw.st_info.type == "STT_TLS": return "tls-offset"
|
||||
return "linked-virtual-address"
|
||||
|
||||
|
||||
def relocation_target(image_type, section_index, offset, sections):
|
||||
if image_type != "ET_REL":
|
||||
return {"kind": "linked-virtual-address", "address": address(offset)}
|
||||
if section_index == 0 or sections[section_index].header.sh_type == "SHT_NULL":
|
||||
return {"kind": "unknown-section", "reported_section_index": section_index,
|
||||
"offset": address(offset), "unknown_reason": "undefined-section-reference" if section_index == 0 else "inactive-section-header"}
|
||||
return {"kind": "section-offset", "section_index": section_index, "offset": address(offset)}
|
||||
|
||||
|
||||
def inspect_elf(path, cache):
|
||||
for package, expected in PACKAGES.items():
|
||||
try:
|
||||
version = importlib.metadata.version(package)
|
||||
except importlib.metadata.PackageNotFoundError as error:
|
||||
raise LayoutFailure("unavailable", "Selected Python lacks " + package + " " + expected + ".") from error
|
||||
if version != expected:
|
||||
raise LayoutFailure("unsupported", "Expected " + package + " " + expected + "; selected Python reports " + version + ".")
|
||||
content = path.read_bytes()
|
||||
if len(content) < 4 or content[:4] != b"\x7fELF":
|
||||
raise LayoutFailure("format", "Selected object does not contain an ELF header.")
|
||||
if len(content) < 16:
|
||||
raise LayoutFailure("format", "Selected ELF identification is truncated.")
|
||||
if content[4] != 2 or content[5] != 1:
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires ELF64 little-endian objects.")
|
||||
if len(content) < 64:
|
||||
raise LayoutFailure("format", "Selected ELF64 header is truncated.")
|
||||
if int.from_bytes(content[18:20], "little") != 62:
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF objects.")
|
||||
if int.from_bytes(content[16:18], "little") not in (1, 2, 3):
|
||||
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
|
||||
from pwnlib.context import context
|
||||
with context.local(cache_dir=str(cache), log_level="warning"):
|
||||
from elftools.common.exceptions import ELFError
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from pwnlib.elf import ELF
|
||||
try:
|
||||
headers = ELFFile(io.BytesIO(content))
|
||||
section_count = headers.num_sections()
|
||||
if headers.header.e_shstrndx == 0xffff and section_count == 0:
|
||||
raise LayoutFailure("format", "ELF extended section-name index requires a declared section-zero header.")
|
||||
raw_name_index = headers.header.e_shstrndx
|
||||
if 0xff00 <= raw_name_index < 0xffff:
|
||||
raise LayoutFailure("format", f"ELF section-name index {raw_name_index} is reserved and cannot directly reference a section.")
|
||||
section_name_index = headers.get_shstrndx()
|
||||
if raw_name_index == 0xffff and section_name_index < 0xff00:
|
||||
raise LayoutFailure("format", f"ELF SHN_XINDEX resolves to ordinary index {section_name_index}; the escape requires an index at least SHN_LORESERVE (0xff00).")
|
||||
if section_name_index != 0:
|
||||
if section_name_index >= section_count:
|
||||
raise LayoutFailure("format", f"ELF section-name table index {section_name_index} lies outside the declared section count {section_count}.")
|
||||
names_header = headers._get_section_header(section_name_index)
|
||||
if names_header.sh_type != "SHT_STRTAB":
|
||||
raise LayoutFailure("format", f"ELF section-name table {section_name_index} is not SHT_STRTAB.")
|
||||
location(names_header.sh_offset, names_header.sh_size, len(content))
|
||||
except ELFError as error:
|
||||
raise LayoutFailure("format", str(error)) from error
|
||||
class LayoutELF(ELF):
|
||||
def _get_section_name(self, header):
|
||||
# SHN_UNDEF declares no names; upstream would otherwise read
|
||||
# strings at the inactive section-zero offset.
|
||||
return "" if section_name_index == 0 else super()._get_section_name(header)
|
||||
|
||||
# pyelftools 0.33 calls these documented filters internally;
|
||||
# pwntools 4.15.0's cached overrides do not accept the argument.
|
||||
# Forward the filter on this adapter instance, without modifying
|
||||
# either upstream package or process-global methods.
|
||||
def iter_segments(self, type=None):
|
||||
return (segment for segment in super().iter_segments()
|
||||
if type is None or segment.header.p_type == type)
|
||||
|
||||
def iter_sections(self, type=None):
|
||||
return (section for section in super().iter_sections()
|
||||
if type is None or section.header.sh_type == type)
|
||||
try:
|
||||
image = LayoutELF(str(path), checksec=False)
|
||||
if image.arch != "amd64" or image.bits != 64 or image.endian != "little":
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF64 little-endian objects.")
|
||||
if image.header.e_type not in ("ET_EXEC", "ET_DYN", "ET_REL"):
|
||||
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
|
||||
length = len(content)
|
||||
all_sections = list(image.iter_sections())
|
||||
shstrings = image.get_section(section_name_index) if all_sections and section_name_index != 0 else None
|
||||
sections, segments, symbols, relocations, packed_relatives = [], [], [], [], []
|
||||
needed, interpreters = [], []
|
||||
for index, section in enumerate(all_sections):
|
||||
h = section.header
|
||||
backing = h.sh_type not in ("SHT_NOBITS", "SHT_NULL")
|
||||
if backing: location(h.sh_offset, h.sh_size, length)
|
||||
sections.append({
|
||||
"index": index, "name": name_reference(section.name, shstrings, h.sh_name, content) if shstrings is not None else {"display": "", "bytes_base64": None, "location": None, "unknown_reason": "ELF declares no section-name table (SHN_UNDEF index 0)."}, "name_offset": address(h.sh_name), "type": h.sh_type,
|
||||
"header_location": location(image.header.e_shoff + index * image.header.e_shentsize, image.header.e_shentsize, length),
|
||||
"address": address(h.sh_addr), "offset": address(h.sh_offset),
|
||||
"size": address(h.sh_size), "alignment": address(h.sh_addralign),
|
||||
"flags": address(h.sh_flags), "link": h.sh_link, "info": h.sh_info,
|
||||
"entry_size": address(h.sh_entsize), "file_backing": "file" if backing else "none",
|
||||
})
|
||||
if h.sh_type in ("SHT_SYMTAB", "SHT_DYNSYM"):
|
||||
if h.sh_entsize < image.structs.Elf_Sym.sizeof():
|
||||
raise LayoutFailure("format", "ELF symbol entry is smaller than the decoded Elf64_Sym structure.")
|
||||
if h.sh_link >= len(all_sections) or all_sections[h.sh_link].header.sh_type != "SHT_STRTAB":
|
||||
raise LayoutFailure("format", f"ELF symbol table {index} links to invalid string table {h.sh_link}.")
|
||||
strings = all_sections[h.sh_link]
|
||||
for entry, symbol in enumerate(section.iter_symbols()):
|
||||
raw = symbol.entry
|
||||
symbols.append({
|
||||
"table_index": index, "entry_index": entry,
|
||||
"name": name_reference(symbol.name, strings, raw.st_name, content), "name_offset": address(raw.st_name),
|
||||
"location": location(h.sh_offset + entry * h.sh_entsize, h.sh_entsize, length),
|
||||
"value": address(raw.st_value), "value_meaning": symbol_value_meaning(raw, image.header.e_type, all_sections),
|
||||
"size": address(raw.st_size), "binding": raw.st_info.bind, "type": raw.st_info.type,
|
||||
"visibility": raw.st_other.visibility, "section_index": raw.st_shndx,
|
||||
})
|
||||
if h.sh_type in ("SHT_REL", "SHT_RELA"):
|
||||
if image.header.e_type == "ET_REL" and h.sh_info >= len(all_sections):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} references missing target section {h.sh_info}.")
|
||||
symbol_table = None
|
||||
if h.sh_link != 0:
|
||||
if h.sh_link >= len(all_sections) or all_sections[h.sh_link].header.sh_type not in ("SHT_SYMTAB", "SHT_DYNSYM"):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} links to invalid symbol table {h.sh_link}.")
|
||||
symbol_table = all_sections[h.sh_link]
|
||||
for entry, relocation in enumerate(section.iter_relocations()):
|
||||
raw = relocation.entry
|
||||
if raw.r_info_sym != 0 and (symbol_table is None or raw.r_info_sym >= symbol_table.num_symbols()):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} entry {entry} references missing symbol {raw.r_info_sym} in table {h.sh_link}.")
|
||||
relocations.append({
|
||||
"section_index": index, "entry_index": entry, "reported_offset": address(raw.r_offset),
|
||||
"target": relocation_target(image.header.e_type, h.sh_info, raw.r_offset, all_sections),
|
||||
"location": location(h.sh_offset + entry * h.sh_entsize, h.sh_entsize, length),
|
||||
"type": raw.r_info_type, "symbol_table_index": h.sh_link, "symbol_index": raw.r_info_sym,
|
||||
"symbol_reference_meaning": "zero-symbol-value" if raw.r_info_sym == 0 else "symbol-table-entry",
|
||||
"addend": None if "r_addend" not in raw else str(raw.r_addend),
|
||||
})
|
||||
if h.sh_type == "SHT_RELR":
|
||||
packed_relatives.append({
|
||||
"section_index": index,
|
||||
"location": location(h.sh_offset, h.sh_size, length),
|
||||
"encoded_bytes_base64": base64.b64encode(content[h.sh_offset:h.sh_offset + h.sh_size]).decode("ascii"),
|
||||
"entries": [{"decoded_index": decoded, "reported_offset": address(relocation.entry.r_offset)} for decoded, relocation in enumerate(section.iter_relocations())],
|
||||
"offset_meaning": "unknown" if image.header.e_type == "ET_REL" else "linked-virtual-address",
|
||||
"evidence_kind": "derived", "entry_source_locations": None, "addends": None,
|
||||
})
|
||||
for index, segment in enumerate(image.iter_segments()):
|
||||
h = segment.header
|
||||
backing = h.p_type != "PT_NULL" and h.p_filesz != 0
|
||||
if backing: location(h.p_offset, h.p_filesz, length)
|
||||
if h.p_type == "PT_LOAD" and h.p_memsz < h.p_filesz:
|
||||
raise LayoutFailure("format", "ELF load segment memory size is smaller than its file size.")
|
||||
segments.append({
|
||||
"index": index, "type": h.p_type, "offset": address(h.p_offset),
|
||||
"header_location": location(image.header.e_phoff + index * image.header.e_phentsize, image.header.e_phentsize, length),
|
||||
"file_size": address(h.p_filesz), "memory_size": address(h.p_memsz),
|
||||
"virtual_address": address(h.p_vaddr), "physical_address": address(h.p_paddr),
|
||||
"alignment": address(h.p_align), "flags": address(h.p_flags),
|
||||
"file_backing": "file" if backing else "none",
|
||||
"permissions": None if h.p_type == "PT_NULL" else {"read": bool(h.p_flags & 4), "write": bool(h.p_flags & 2), "execute": bool(h.p_flags & 1)},
|
||||
})
|
||||
if h.p_type == "PT_DYNAMIC":
|
||||
tag_size = image.structs.Elf_Dyn.sizeof()
|
||||
tags = []
|
||||
for tag_index in range(h.p_filesz // tag_size):
|
||||
tag = segment._get_tag(tag_index)
|
||||
tags.append(tag)
|
||||
if tag.d_tag == "DT_NULL":
|
||||
break
|
||||
else:
|
||||
raise LayoutFailure("format", f"ELF dynamic segment {index} has no complete DT_NULL terminator within its reported file range.")
|
||||
for tag in tags:
|
||||
if tag.d_tag == "DT_NEEDED":
|
||||
needed.append(dynamic_name_reference(tags, tag.d_val, image, content))
|
||||
if h.p_type == "PT_INTERP":
|
||||
raw = content[h.p_offset:h.p_offset + h.p_filesz]
|
||||
end = raw.find(b"\0")
|
||||
if end < 0:
|
||||
raise LayoutFailure("format", "ELF interpreter lacks a reported terminator.")
|
||||
interpreters.append({"display": raw[:end].decode("utf-8", "replace"), "bytes_base64": base64.b64encode(raw[:end]).decode("ascii"), "location": location(h.p_offset, end + 1, length), "unknown_reason": None})
|
||||
return {
|
||||
"format": "elf", "architecture": {"machine": image.header.e_machine, "bits": image.bits, "byte_order": image.endian},
|
||||
"image_type": image.header.e_type,
|
||||
"entry_point": {"reported_value": address(image.header.e_entry), "meaning": "not-applicable" if image.header.e_type == "ET_REL" else "absent" if image.header.e_entry == 0 else "linked-virtual-address", "execution_status": "unknown"},
|
||||
"runtime_load_base": None, "sections": sections, "segments": segments, "symbols": symbols, "relocations": relocations,
|
||||
"packed_relative_relocations": packed_relatives, "relocation_inventory_completeness": "unknown",
|
||||
"linkage": {"needed_libraries": needed, "interpreters": interpreters,
|
||||
"got": [{"display_name": name, "address": address(value)} for name, value in image.got.items()],
|
||||
"plt": [{"display_name": name, "address": address(value)} for name, value in image.plt.items()],
|
||||
"convenience_maps_completeness": "unknown", "runtime_library_paths": None},
|
||||
"mitigations": {"evidence_kind": "inferred", "position_independent": image.pie, "nx_indicator": image.nx, "executable_stack_indicator": image.execstack, "stack_canary_indicator": image.canary, "relro": image.relro},
|
||||
"limitations": [
|
||||
"PT_NULL payload fields are unused and retain their reported numbers without file-byte or permission claims. Zero-file-size segments provide no file bytes.",
|
||||
"SHN_XINDEX symbol values remain unresolved in this upstream representation; an external index table does not establish a resolved index in this report.",
|
||||
"Reported values preserve linked addresses, section offsets, TLS offsets, alignment and absolute values separately. Runtime load base and library paths are unknown.",
|
||||
"Names are display strings plus raw bytes and file ranges where resolvable; section/table/entry indices preserve identity and duplicates. Name ranges include their terminating NUL; raw name bytes exclude it.",
|
||||
"Ordinary relocation rows cover REL/RELA. RELR tables retain complete encoded bytes and upstream-decoded offsets as derived evidence; per-offset packed-word locations and implicit addends remain unknown. Relocation inventory completeness is unknown, including other encodings and missing section tables.",
|
||||
"Relocation symbol index zero uses a zero symbol value without a table lookup. Positive indices reference validated original symbol-table entries; raw table and symbol indices remain reported.",
|
||||
"Relocatable relocation targets with SHN_UNDEF or inactive SHT_NULL headers retain their reported section index/offset as unknown; they do not establish an associated target section.",
|
||||
"GOT/PLT are derived upstream convenience maps; PLT inference can emulate selected instructions in Unicorn. They do not establish target runtime behavior; aliases may collapse and warnings may indicate incomplete coverage. Their completeness is unknown.",
|
||||
"Mitigations are upstream static heuristics, not runtime protection. ET_DYN does not prove an executable; absent canary symbols do not prove every function unprotected.",
|
||||
"This profile inspects ELF EXEC/DYN/REL layout only. It does not analyze recorded cores, resolve loaded libraries, launch the target as a host process or start a debugger.",
|
||||
"Symbol/relocation inventories reflect original section tables. A sectionless image can still report dynamic dependency names; missing tables do not prove absence of dynamic symbols or relocations.",
|
||||
*(["Absent section-name table: section-dependent RELRO/canary heuristics may be incomplete; protection coverage is unknown."] if all_sections and section_name_index == 0 else []),
|
||||
*(["Sectionless images lack the .dynamic section and symbol tables used by upstream heuristics. RELRO/canary indicators may be incomplete; their values remain reported static candidates, with protection coverage unknown."] if not all_sections else []),
|
||||
],
|
||||
}
|
||||
except LayoutFailure:
|
||||
raise
|
||||
except (ELFError, ValueError, AssertionError, IndexError) as error:
|
||||
raise LayoutFailure("format", "Selected ELF failed unchanged upstream structural parsing: " + str(error)) from error
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime_path = Path(__file__).with_name("decoder_runtime.py")
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
runtime["main"](sys.argv[-1], inspect_elf, LayoutFailure, PROFILE)
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Catch typed resource failures while loading or running the owned recorded-core adapter.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "recorded_crash_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -0,0 +1,147 @@
|
||||
"""Bounded Linux core evidence via unchanged ELFFile/structs and pwntools ctypes.
|
||||
|
||||
No Corefile-selected thread, executable guessing, process lookup or target launch.
|
||||
"""
|
||||
import base64
|
||||
import ctypes
|
||||
import importlib.metadata
|
||||
import io
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
|
||||
|
||||
|
||||
class CoreFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def encoded(value):
|
||||
return base64.b64encode(value).decode("ascii")
|
||||
|
||||
|
||||
def location(offset, size, limit):
|
||||
if not 0 <= offset <= limit or not 0 <= size <= limit - offset:
|
||||
raise CoreFailure("format", "Recorded source range exceeds its declared container.")
|
||||
return {"offset": hex(offset), "bytes": hex(size)}
|
||||
|
||||
|
||||
def inspect_core(path, cache):
|
||||
for package, required in {"pwntools": "4.15.0", "pyelftools": "0.33", "unicorn": "2.1.2"}.items():
|
||||
try:
|
||||
actual = importlib.metadata.version(package)
|
||||
except importlib.metadata.PackageNotFoundError as error:
|
||||
raise CoreFailure("unavailable", "Selected Python lacks " + package + " " + required + ".") from error
|
||||
if actual != required:
|
||||
raise CoreFailure("unsupported", "Expected " + package + " " + required + "; selected Python reports " + actual + ".")
|
||||
content = path.read_bytes()
|
||||
if len(content) < 16 or content[:4] != b"\x7fELF":
|
||||
raise CoreFailure("format", "Selected recording lacks a complete ELF identification.")
|
||||
if content[4:6] != b"\x02\x01":
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires ELF64 little-endian.")
|
||||
if len(content) < 64:
|
||||
raise CoreFailure("format", "Selected ELF64 core header is truncated.")
|
||||
if content[6] != 1:
|
||||
raise CoreFailure("format", "ELF identification version is not EV_CURRENT.")
|
||||
if int.from_bytes(content[20:24], "little") != 1:
|
||||
raise CoreFailure("format", "ELF header version is not EV_CURRENT.")
|
||||
# Linux commonly emits ELFOSABI_NONE (0), also accepted as GNU/Linux (3).
|
||||
# Other declared OSABIs must never select Linux note ctypes.
|
||||
if content[7] not in (0, 3) or content[8] != 0:
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires System V or GNU/Linux OSABI with ABI version zero; selected OSABI=" + str(content[7]) + ", ABI version=" + str(content[8]) + ".")
|
||||
if int.from_bytes(content[16:18], "little") != 4 or int.from_bytes(content[18:20], "little") != 62:
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires x86-64 ET_CORE.")
|
||||
from elftools.common.exceptions import ELFError
|
||||
from elftools.common.utils import struct_parse
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from pwnlib.elf.datatypes import elf_prstatus_amd64, elf_siginfo_64, user_regs_struct_amd64
|
||||
try:
|
||||
image = ELFFile(io.BytesIO(content))
|
||||
header = image.header
|
||||
count = image.num_segments()
|
||||
if count and header.e_phentsize < image.structs.Elf_Phdr.sizeof():
|
||||
raise CoreFailure("format", "Program-header entry is shorter than the required ELF64 structure.")
|
||||
location(header.e_phoff, count * header.e_phentsize, len(content))
|
||||
segments, notes, padding, threads, signals = [], [], [], [], []
|
||||
for segment_index, segment in enumerate(image.iter_segments()):
|
||||
h = segment.header
|
||||
backing = h.p_type != "PT_NULL" and h.p_filesz > 0
|
||||
if backing:
|
||||
location(h.p_offset, h.p_filesz, len(content))
|
||||
if h.p_type == "PT_LOAD" and h.p_filesz > h.p_memsz:
|
||||
raise CoreFailure("format", "Recorded PT_LOAD file bytes exceed memory size.")
|
||||
segments.append({"index": segment_index, "type": h.p_type,
|
||||
"header_location": location(header.e_phoff + segment_index * header.e_phentsize, header.e_phentsize, len(content)),
|
||||
"offset": hex(h.p_offset), "file_size": hex(h.p_filesz), "memory_size": hex(h.p_memsz),
|
||||
"virtual_address": hex(h.p_vaddr), "physical_address": hex(h.p_paddr), "alignment": hex(h.p_align), "flags": hex(h.p_flags), "file_backing": "file" if backing else "none"})
|
||||
if h.p_type != "PT_NOTE" or h.p_filesz == 0:
|
||||
continue
|
||||
position, end = h.p_offset, h.p_offset + h.p_filesz
|
||||
header_size = image.structs.Elf_Nhdr.sizeof()
|
||||
while position < end:
|
||||
if end - position < header_size:
|
||||
raw = content[position:end]
|
||||
if any(raw):
|
||||
raise CoreFailure("format", "Nonzero trailing note bytes lack a complete note header.")
|
||||
padding.append({"segment_index": segment_index, "location": location(position, len(raw), end), "bytes_base64": encoded(raw)})
|
||||
break
|
||||
note = struct_parse(image.structs.Elf_Nhdr, image.stream, stream_pos=position)
|
||||
owner_start = position + header_size
|
||||
descriptor_start = owner_start + ((note.n_namesz + 3) // 4) * 4
|
||||
note_end = descriptor_start + ((note.n_descsz + 3) // 4) * 4
|
||||
location(position, note_end - position, end)
|
||||
owner = content[owner_start:owner_start + note.n_namesz]
|
||||
if owner and b"\0" not in owner:
|
||||
raise CoreFailure("format", "Declared note owner has no NUL terminator.")
|
||||
display = owner.split(b"\0", 1)[0].decode("utf-8", "replace") if owner else None
|
||||
descriptor = content[descriptor_start:descriptor_start + note.n_descsz]
|
||||
note_index = len(notes)
|
||||
notes.append({"index": note_index, "segment_index": segment_index, "type": note.n_type,
|
||||
"location": location(position, note_end - position, end),
|
||||
"owner_location": location(owner_start, len(owner), end), "owner_bytes_base64": encoded(owner), "owner_display": display,
|
||||
"descriptor_location": location(descriptor_start, len(descriptor), end), "descriptor_bytes_base64": encoded(descriptor)})
|
||||
position = note_end
|
||||
# Only the initial Linux CORE ABI is interpreted. Other owners retain exact bytes.
|
||||
if owner != b"CORE\0":
|
||||
continue
|
||||
if note.n_type == "NT_PRSTATUS":
|
||||
if len(descriptor) < ctypes.sizeof(elf_prstatus_amd64):
|
||||
raise CoreFailure("format", "Linux amd64 NT_PRSTATUS descriptor is truncated.")
|
||||
status = elf_prstatus_amd64.from_buffer_copy(descriptor)
|
||||
registers = []
|
||||
for name, kind in user_regs_struct_amd64._fields_:
|
||||
relative = elf_prstatus_amd64.pr_reg.offset + getattr(user_regs_struct_amd64, name).offset
|
||||
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
|
||||
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
|
||||
elif note.n_type == "NT_SIGINFO":
|
||||
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
|
||||
raise CoreFailure("format", "Linux amd64 NT_SIGINFO descriptor is truncated.")
|
||||
signal = elf_siginfo_64.from_buffer_copy(descriptor)
|
||||
number = ctypes.c_int32(signal.si_signo).value
|
||||
code = ctypes.c_int32(signal.si_code).value
|
||||
known = number == 11 and code in (1, 2)
|
||||
signals.append({"note_index": note_index, "number": number, "code": code, "errno": ctypes.c_int32(signal.si_errno).value,
|
||||
"fault_address": hex(signal.sigfault_addr) if known else None,
|
||||
"fault_address_meaning": "recorded-sigsegv-address" if known else "unknown", "thread_association": "unknown"})
|
||||
return {"format": "elf-core", "architecture": "x86_64-little-endian", "target_execution": "not-performed", "live_process_identity": "unknown",
|
||||
"segments": segments, "notes": notes, "note_padding": padding, "threads": threads, "signals": signals,
|
||||
"note_interpretation_completeness": "unknown",
|
||||
"limitations": ["The initial Linux note profile accepts System V/unspecified or GNU/Linux OSABI with ABI version zero; an unspecified OSABI alone does not prove operating-system origin.",
|
||||
"Historical PID and signal values are recorded metadata, not current process identity or attach authority.",
|
||||
"Only exact Linux CORE NT_PRSTATUS/NT_SIGINFO owners are interpreted; every descriptor and owner retains original bytes. Other note semantics remain unknown.",
|
||||
"NT_SIGINFO thread association is unknown. Only SIGSEGV SEGV_MAPERR/SEGV_ACCERR initially establish a fault-address union meaning.",
|
||||
"PT_LOAD file bytes are recorded bytes. Undumped memory and current executable/library identity remain unknown; no current host file is resolved from a recorded pathname.",
|
||||
"Missing notes/registers/signals do not establish their absence at the original crash. Full unwinding, executable pairing and live control are not provided."]}
|
||||
except CoreFailure:
|
||||
raise
|
||||
except (ELFError, ValueError, AssertionError, IndexError) as error:
|
||||
raise CoreFailure("format", "Recorded core failed unchanged upstream structural parsing: " + str(error)) from error
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime_path = Path(__file__).with_name("decoder_runtime.py")
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
runtime["main"](sys.argv[-1], inspect_core, CoreFailure, PROFILE)
|
||||
@@ -0,0 +1,85 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
|
||||
export default defineConfig({
|
||||
title: "REA",
|
||||
description:
|
||||
"Reverse engineer binaries, applications, and runtime behavior with one CLI and MCP server.",
|
||||
lang: "en-US",
|
||||
base: "/rea/",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Get started", link: "/installation" },
|
||||
{ text: "Reference", link: "/mcp-contracts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
text: "Getting started",
|
||||
items: [
|
||||
{ text: "Installation and setup", link: "/installation" },
|
||||
{ text: "CLI and Evidence", link: "/cli" },
|
||||
{ text: "Agent prompts", link: "/mcp-prompts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Investigation guides",
|
||||
items: [
|
||||
{ text: "Native binaries", link: "/native-investigation" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
link: "/javascript-artifact-reconstruction",
|
||||
},
|
||||
{
|
||||
text: "JavaScript workflows",
|
||||
link: "/javascript-application-workflows",
|
||||
},
|
||||
{ text: "Browser observation", link: "/browser-observation" },
|
||||
{ text: "Electron observation", link: "/electron-observation" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{ text: "Android applications", link: "/android-analysis" },
|
||||
{ text: "Apple applications", link: "/apple-application-analysis" },
|
||||
{ text: "Firmware", link: "/firmware-analysis" },
|
||||
{
|
||||
text: "Reconstruction readiness",
|
||||
link: "/reconstruction-readiness",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Provider guides",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "IDA", link: "/ida-provider" },
|
||||
{ text: "Ghidra on Windows", link: "/windows-ghidra-p0" },
|
||||
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
|
||||
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
|
||||
{ text: "Provider evaluation", link: "/provider-evaluation" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Reference and development",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "MCP contracts", link: "/mcp-contracts" },
|
||||
{ text: "Tool design", link: "/tool-design" },
|
||||
{ text: "Testing", link: "/testing" },
|
||||
{ text: "Releasing", link: "/releasing" },
|
||||
{ text: "Architecture decisions", link: "/adr/README" },
|
||||
],
|
||||
},
|
||||
],
|
||||
search: { provider: "local" },
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/morluto/rea" },
|
||||
{ icon: "discord", link: "https://discord.gg/GkcryMnJDM" },
|
||||
],
|
||||
editLink: {
|
||||
pattern: "https://github.com/morluto/rea/edit/main/docs/:path",
|
||||
},
|
||||
footer: {
|
||||
message: "Released under the MIT License.",
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -5,9 +5,12 @@
|
||||
- Implementation status: The provider registry, deterministic selection,
|
||||
target binding, analysis-profile commitment, and snapshot/Evidence migration
|
||||
are implemented. Ghidra discovery, target/profile resolution, doctor checks,
|
||||
the private headless-session foundation, ten read-only inventory capabilities,
|
||||
and twelve function-analysis capabilities are implemented with real
|
||||
cross-format conformance.
|
||||
the private headless-session foundation, thirteen read-only inventory/load-image
|
||||
capabilities, and twelve function-analysis capabilities are implemented with
|
||||
real cross-format conformance. Linux/macOS also supports atomic session
|
||||
function annotations; experimental Windows x64 P0 uses bundled native controls
|
||||
and admits only the 25 read-only operations. See the
|
||||
[release boundary](../installation.md#released-package-and-main).
|
||||
|
||||
## Context
|
||||
|
||||
@@ -25,13 +28,13 @@ engine, engine version, or complete analysis configuration. Two analyses of the
|
||||
same bytes could therefore disagree because of language, compiler, loader,
|
||||
analyzer, or provider-version differences while appearing target-compatible.
|
||||
|
||||
These constraints are embodied in the current
|
||||
[`CompositeProvider`](../../src/application/CompositeProvider.ts),
|
||||
[runtime composition](../../src/application/runtime.ts),
|
||||
[`BinarySession`](../../src/application/BinarySession.ts),
|
||||
[`BinaryTarget`](../../src/domain/binaryTarget.ts),
|
||||
[analysis snapshot](../../src/domain/analysisSnapshot.ts), and
|
||||
[session lifecycle inputs](../../src/contracts/sessionLifecycleInputs.ts).
|
||||
The decision addressed the earlier representations of
|
||||
[`CompositeProvider`](https://github.com/morluto/rea/blob/main/src/application/binary/CompositeProvider.ts),
|
||||
[runtime composition](https://github.com/morluto/rea/blob/main/src/composition/binary.ts),
|
||||
[`BinarySession`](https://github.com/morluto/rea/blob/main/src/application/binary/BinarySession.ts),
|
||||
[`BinaryTarget`](https://github.com/morluto/rea/blob/main/src/domain/binaryTarget.ts),
|
||||
[analysis snapshot](https://github.com/morluto/rea/blob/main/src/domain/analysisSnapshot.ts), and
|
||||
[session lifecycle inputs](https://github.com/morluto/rea/blob/main/src/contracts/sessionLifecycleInputs.ts).
|
||||
|
||||
The next deep provider must not weaken the existing product invariants:
|
||||
|
||||
@@ -249,7 +252,7 @@ No request can cross a concurrent target or provider transition.
|
||||
canonical path, source digest, generic kind and format, selected architecture,
|
||||
and available architectures. Hopper loader arguments leave the domain model.
|
||||
Provider-owned databases use a generic analysis-database classification; their
|
||||
adapter determines support. A legacy `.hop` target is therefore accepted only
|
||||
adapter determines support. A `.hop` analysis database is therefore accepted only
|
||||
by Hopper without teaching the domain how to open it. Ghidra v1 continues to use
|
||||
private temporary projects and does not open user projects.
|
||||
|
||||
@@ -333,19 +336,19 @@ entries, so snapshots must be recaptured before they can be used as a cache.
|
||||
An embedded Evidence bundle can still be imported separately through the
|
||||
Evidence import flow; it is never promoted into an analysis cache.
|
||||
|
||||
### 8. Add profile commitments to new deep Evidence while preserving legacy records
|
||||
### 8. Commit concrete or explicitly unknown analysis profiles in Evidence
|
||||
|
||||
The optional `analysis_profile` field uses the canonical envelope above. Every
|
||||
newly created deep-analysis observation must include it, and its provider ID
|
||||
and version must match the record's provider. The field participates in the
|
||||
Evidence semantic ID.
|
||||
Every Evidence record includes `analysis_profile`, either the canonical profile
|
||||
envelope above or `null` when no profile was observed. Deep-analysis observations
|
||||
commit the resolved provider ID, version, and effective configuration in this
|
||||
profile. The field participates in the Evidence semantic ID.
|
||||
|
||||
Existing Evidence records without `analysis_profile` remain valid and keep
|
||||
their existing IDs. Absence on a legacy deep record means the analysis profile
|
||||
is unknown; REA does not infer or enrich it during import. Such evidence can be
|
||||
displayed and cited but cannot establish profile compatibility, seed snapshot
|
||||
replay, or support an "unchanged" conclusion that depends on identical analysis
|
||||
semantics.
|
||||
An explicitly unknown profile can be displayed and cited, but cannot establish
|
||||
profile compatibility, seed snapshot replay, or support an "unchanged"
|
||||
conclusion that depends on identical analysis semantics. Snapshot query entries
|
||||
must have matching profile-bound Evidence; imports reject missing or mismatched
|
||||
observations instead of silently dropping query entries. Both `entries` and
|
||||
`workflow_entries` are required arrays, including when empty.
|
||||
|
||||
Different providers or profile digests are never snapshot-compatible. A
|
||||
derived comparison may compare cross-provider normalized facts only when its
|
||||
@@ -404,12 +407,12 @@ means no dynamic provider has supplied a snapshot. Once one or more start,
|
||||
snapshots are not live inventories, and a point-in-time observation never proves
|
||||
historical absence.
|
||||
|
||||
The existing flat `provider`, `providers`, and `capabilities` status fields stay
|
||||
during the 1.x compatibility window. Their effective deep-operation entries
|
||||
come only from the active binding; target-free ambiguity is unavailable rather
|
||||
than arbitrarily collapsed. The new candidate fields are authoritative for
|
||||
selection. A future major release may remove the synthetic composite provider
|
||||
field after callers have migrated.
|
||||
Status exposes `analysis_provider_candidates` for configured deep providers and
|
||||
`analysis_provider_binding` for the selected concrete provider and profile.
|
||||
`capabilities` retains effective operation availability and exact provider
|
||||
provenance, including auxiliary operation families. There are no synthetic
|
||||
flat `provider` or `providers` aliases. Provider prompt completion uses the
|
||||
actual selectable candidates.
|
||||
|
||||
Selection failures extend the tagged `ProviderSelectionError` with a stable
|
||||
reason:
|
||||
@@ -445,26 +448,20 @@ snapshot's provider.
|
||||
- Tool names and normalized output contracts remain provider-neutral. REA does
|
||||
not add `ghidra_decompile` or `hopper_decompile` tools.
|
||||
|
||||
## Compatibility and migration
|
||||
## Provider and snapshot representations
|
||||
|
||||
| Existing behavior or data | Decision |
|
||||
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| No provider selector, only Hopper usable | `auto` selects the sole candidate; behavior remains equivalent |
|
||||
| No deep provider usable | Automatic open may remain unbound for auxiliary operations; deep capabilities explain why they are unavailable |
|
||||
| Hopper and Ghidra both usable, no preference | Open fails with `ambiguous`; installation order never becomes policy |
|
||||
| Existing Hopper environment settings | Remain supported as Hopper adapter configuration |
|
||||
| `open_binary` and CLI structured `loaderArgs` output | Retained as a deprecated Hopper-only compatibility projection for 1.x; the analysis profile is authoritative |
|
||||
| `BinaryTarget.loaderArgs` | Removed; Hopper derives them inside its adapter and commits normalized semantics in the profile |
|
||||
| Legacy `.hop` input | Classified generically as an analysis database and accepted only by Hopper |
|
||||
| Snapshot v1 | Not accepted as the current snapshot format; recapture before cache replay |
|
||||
| Existing Evidence without a profile | Accepted unchanged; profile compatibility is unknown |
|
||||
| New deep-analysis Evidence | Must include `analysis_profile`; its semantic ID commits the field |
|
||||
| Existing status fields | Retained for 1.x with additive binding and candidate fields |
|
||||
| Provider failure after selection | Returned from the selected provider; no transparent retry through another engine |
|
||||
|
||||
The deprecated `loaderArgs` response must never be read to select a provider or
|
||||
validate a snapshot. It is empty for non-Hopper bindings and may be removed only
|
||||
with a separately documented major contract change.
|
||||
| Behavior or data | Contract |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| No selector, one usable deep provider | `auto` selects the sole candidate |
|
||||
| No usable deep provider | Automatic open may remain unbound for auxiliary operations; deep capabilities explain their unavailability |
|
||||
| Several usable deep providers without a preference | Open fails with `ambiguous`; installation order never becomes policy |
|
||||
| Hopper environment settings | Adapter configuration committed into the typed analysis profile, including effective loader arguments |
|
||||
| `open_binary` output | Provider-neutral target identity only; no `loaderArgs` projection |
|
||||
| `.hop` input | Analysis database accepted only by Hopper |
|
||||
| Snapshots | Concrete provider/profile binding, required primitive and workflow entry arrays, and matching authenticated Evidence |
|
||||
| Evidence without an observed profile | Explicit `analysis_profile: null`; ineligible for profile-dependent replay |
|
||||
| Snapshot save receipt | `primitive_entries`, `workflow_entries`, and `evidence_records` counts without a duplicate `entries` alias |
|
||||
| Provider failure after selection | Returned from that provider, without transparent retry through another engine |
|
||||
|
||||
## Required behavior scenarios
|
||||
|
||||
@@ -569,5 +566,7 @@ The first six implementation stages are shipped:
|
||||
|
||||
Future stages may deepen format and indirect-flow coverage, but must continue
|
||||
to compare normalized semantics rather than provider-specific pseudocode or
|
||||
assembly text. Ghidra continues to omit GUI and mutation operations, so
|
||||
unsupported requests cannot route to an unverified implementation.
|
||||
assembly text. Linux and macOS now admit atomic function-name and entry-comment
|
||||
edits in the ephemeral database, with readback, rollback, and cache invalidation.
|
||||
Executable bytes remain unchanged. Windows P0 retains its read-only boundary;
|
||||
GUI and other unimplemented mutations remain unavailable.
|
||||
|
||||
+5
-5
@@ -4,8 +4,8 @@ Accepted decisions describe the architecture that implementation PRs must
|
||||
follow. Acceptance does not by itself mean the behavior is shipped; each record
|
||||
states its implementation status separately.
|
||||
|
||||
| ADR | Status | Implementation |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | -------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| [0001: Provider selection and analysis profiles](0001-provider-selection-and-analysis-profiles.md) | Accepted | Provider selection and 22 Ghidra operations shipped on Linux/macOS; Windows analysis blocked |
|
||||
| [0002: Controlled JavaScript replay authority and sandbox policy](0002-controlled-replay-authority-and-sandbox.md) | Superseded | Historical design; the controlled JavaScript replay tool was removed |
|
||||
| [0003: Managed-code evidence and provider boundary](0003-managed-code-evidence-and-provider-boundary.md) | Partially superseded | Seven static managed tools remain; runtime planning removed |
|
||||
| ADR | Status | Implementation |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [0001: Provider selection and analysis profiles](0001-provider-selection-and-analysis-profiles.md) | Accepted | Provider selection and 25 read-only Ghidra operations on Linux/macOS and experimental Windows x64 P0; Linux/macOS session annotations |
|
||||
| [0002: Controlled JavaScript replay authority and sandbox policy](0002-controlled-replay-authority-and-sandbox.md) | Superseded | Historical design; the controlled JavaScript replay tool was removed |
|
||||
| [0003: Managed-code evidence and provider boundary](0003-managed-code-evidence-and-provider-boundary.md) | Partially superseded | Seven static managed tools remain; runtime planning removed |
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
# Static Android APK analysis
|
||||
|
||||
REA wraps an existing headless JADX engine through `src/android/`. Its source is
|
||||
kept as an unmodified, commit-pinned Git submodule; see
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md). CLI and MCP use the same
|
||||
application workflow and return artifact-bound Evidence inline.
|
||||
|
||||
This family is present on repository main and in npm 4.1.0. Check the
|
||||
[released package boundary](installation.md#released-package-and-main) and the
|
||||
connected server's tool list before selecting it.
|
||||
|
||||
## Supply tools
|
||||
|
||||
Use a **full JDK 17 or newer** (including its compiler module) and
|
||||
**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
|
||||
source-file mode against the unmodified engine JAR. REA does not install Java,
|
||||
an Android SDK, JADX, a device service or an emulator. Set the path in the shell
|
||||
or MCP server environment:
|
||||
|
||||
```sh
|
||||
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
|
||||
# Optional: select an existing JDK rather than java on PATH.
|
||||
export JAVA_HOME=/absolute/path/existing-jdk
|
||||
```
|
||||
|
||||
On Windows x64, use a REA package with its matching bundled Windows native
|
||||
controls **including owned protocol stdin support**. This Windows boundary is
|
||||
new on repository main; an older released package can still reject this host.
|
||||
PowerShell accepts paths containing spaces without additional escaping
|
||||
inside these environment values:
|
||||
|
||||
```powershell
|
||||
$env:REA_JADX_MCP_JAR = 'D:\tools\jadx-headless-mcp-0.7.1-all.jar'
|
||||
$env:JAVA_HOME = 'D:\tools\jdk-21'
|
||||
rea inspect-android-package 'D:\targets\Example.apk'
|
||||
```
|
||||
|
||||
REA selects `JAVA_HOME\bin\java.exe`, or resolves `java.exe` on the selected
|
||||
Windows `PATH`. A missing or mismatched native artifact reports the package
|
||||
prerequisite rather than attempting an unowned Java launch. Windows arm64 is
|
||||
outside this provider's current native boundary.
|
||||
|
||||
Readiness checks the selected JAR's ZIP directory for the classes consumed by
|
||||
REA's metadata bridge, as well as Java's compiler and version modules. It does
|
||||
not load an APK or execute engine code. Invalid archives and missing bridge
|
||||
classes report JAR-specific recovery; classpath inventory alone does not prove
|
||||
that every class or engine operation will load successfully. Discovery limits
|
||||
each ZIP metadata read to 8 MiB before allocation; a larger directory reports
|
||||
the exhausted read budget and the selected JAR path.
|
||||
|
||||
The current metadata bridge is verified on macOS arm64 with OpenJDK 21 and the
|
||||
public Appium ApiDemos fixture. The POSIX adapter also supports Linux; the new
|
||||
bridge has not yet undergone real verification on Linux/JDK 25.
|
||||
Windows x64 uses an owned Job Object with bidirectional stdio, atomic job
|
||||
assignment and kill-on-owner-close. Real Windows verification uses JDK 21 and
|
||||
the pinned JADX 0.7.1 JAR; see the real verification lane below.
|
||||
|
||||
## Answer an analyst question
|
||||
|
||||
```sh
|
||||
rea inspect-android-package /path/Example.apk
|
||||
rea search-android-classes /path/Example.apk MainActivity
|
||||
rea inspect-android-class /path/Example.apk example.MainActivity
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity onCreate
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity select --overload-index 1
|
||||
rea trace-android-references /path/Example.apk example.MainActivity
|
||||
rea trace-android-references /path/Example.apk example.MainActivity --method-name onCreate
|
||||
```
|
||||
|
||||
Equivalent MCP methods are `inspect_android_package`, `search_android_classes`,
|
||||
`inspect_android_class`, `inspect_android_method` and `trace_android_references`.
|
||||
Every request includes `path`. Class/method operations use `class_name`,
|
||||
`method_name` and optional `overload_index` as applicable. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_android_method",
|
||||
"arguments": {
|
||||
"path": "/path/Example.apk",
|
||||
"class_name": "example.MainActivity",
|
||||
"method_name": "select",
|
||||
"overload_index": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `inspect_android_class` to choose an overload. REA rejects an ambiguous
|
||||
method request rather than silently selecting the first match. The engine's
|
||||
method reference operation cannot select overloads, so REA reports that boundary
|
||||
as unsupported when multiple same-name methods exist.
|
||||
|
||||
The engine's smali fallback also joins same-name overloads; REA rejects that
|
||||
fallback for an overloaded method instead of attributing all bodies to one
|
||||
selected overload. Native/abstract methods with no decompiled body report
|
||||
`body_status: "not_available"` without inferring which implementation flag caused it.
|
||||
|
||||
## Interpret the result
|
||||
|
||||
- Package inspection retains decoded manifest XML, summary declarations,
|
||||
permissions and class/resource counts. Summary fields are extracted by the
|
||||
upstream engine and should be checked against the retained XML when needed.
|
||||
Signature verification is explicitly `not_performed`.
|
||||
- Class search consumes all pages and checks their counts. An empty `query`
|
||||
inventories all class names; class names are decompiler representations.
|
||||
- Class inventories read parsed metadata without generating source and return
|
||||
full reported display types, fields and inner class names. Synthetic members
|
||||
that code generation hides can appear in the inventory; it stays consistent
|
||||
after method decompilation within the retained session.
|
||||
Exact DEX descriptors are unknown. Overload indices are tied to this artifact
|
||||
and engine, not stable identities across different builds.
|
||||
- Method inspection reports Java or smali, fallback markers and complete/partial
|
||||
returned text. Decompiled source is a derived representation. A complete text
|
||||
result does not establish complete semantics or a byte-identical reconstruction.
|
||||
- Reference results are incoming static relationships between provider nodes.
|
||||
Source lines and DEX instruction offsets remain unknown. They are not observed
|
||||
runtime calls.
|
||||
- Original requests, APK SHA-256, actual engine JAR SHA-256 and raw producer
|
||||
responses remain in Evidence. Source revision is populated only for bytes
|
||||
matching the audited release JAR.
|
||||
|
||||
These operations do not execute the APK or provide split APK/AAB handling,
|
||||
signature validation, full resource-table semantics, native-library analysis or
|
||||
Android runtime capture. Existing artifact inventory/extraction tools can supply
|
||||
archive evidence. Project that Evidence with the execution-free inventory tool:
|
||||
|
||||
```sh
|
||||
rea project-android-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project_android_application_graph",
|
||||
"arguments": {
|
||||
"inventory_evidence": ["<inventory_artifact Evidence>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The projection reports exact component paths and hashes, runtime-family hints,
|
||||
and path-based bridge hypotheses. It does not decode DEX or claim observed
|
||||
runtime calls. Bridge hypotheses form a DEX/JVM-class by native-library
|
||||
Cartesian product. The projection computes the product and exact serialized
|
||||
candidate-array size before creating candidate objects, then retains a
|
||||
deterministic prefix within a 2 MiB UTF-8 JSON budget. If candidates are omitted,
|
||||
`bridge_candidate_coverage` reports total, emitted, and omitted counts; overall
|
||||
coverage is `partial` while component arrays still include every component
|
||||
from the supplied inventory pages. A path pair does not establish that a
|
||||
managed declaration names, loads, or calls the native library.
|
||||
|
||||
## Resource and lifecycle limits
|
||||
|
||||
REA retains one serialized session with private, immutable APK, engine JAR and
|
||||
metadata-bridge copies. Matching requests reuse the loaded engine. APK/engine
|
||||
digests and JVM configuration are checked before reuse; changed inputs retire
|
||||
the previous session. Sessions are cleaned after 60 seconds of inactivity, on
|
||||
target/configuration changes, failure, timeout, cancellation, MCP disconnect or
|
||||
server shutdown. One-shot CLI commands always join cleanup before returning.
|
||||
REA never writes to the original APK or launches target code.
|
||||
|
||||
On Windows, stdin writes preserve stream backpressure and run outside the Node
|
||||
event loop. Cancellation terminates the retained job, including descendants,
|
||||
before workspace cleanup. An abrupt CLI/server process termination closes the
|
||||
job and stops Java, but cannot run JavaScript workspace cleanup; temporary
|
||||
workspace files can remain after that forced exit.
|
||||
|
||||
If Java cannot start the bridge, REA reports the selected executable and retained
|
||||
startup diagnostics. Select a full JDK through `JAVA_HOME` or `PATH`, and use
|
||||
`java --list-modules` to confirm `jdk.compiler` is present. A runnable JRE alone
|
||||
cannot compile the bridge. Missing or ambiguous class and method selections are
|
||||
input failures; discover names with `search_android_classes` and
|
||||
`inspect_android_class` before correcting the selected argument.
|
||||
|
||||
If process cleanup cannot be confirmed, REA retains the workspace and reports its
|
||||
location. That provider instance blocks subsequent engine launches until the
|
||||
caller resolves the reported resources and starts a fresh instance.
|
||||
|
||||
The provider uses one JADX analysis worker independently of JVM processor and
|
||||
garbage-collector settings. REA supplies no default heap or visible-CPU override;
|
||||
Java uses its normal ergonomics and inherits caller JVM settings. Optional
|
||||
provider settings add explicit JVM arguments:
|
||||
|
||||
```sh
|
||||
export REA_JADX_HEAP_MIB=8192
|
||||
# Optional, independent of the single JADX worker:
|
||||
export REA_JADX_ACTIVE_PROCESSOR_COUNT=4
|
||||
# Or select settings through Java's standard environment options:
|
||||
export JAVA_TOOL_OPTIONS=-Xmx8g
|
||||
```
|
||||
|
||||
Both REA settings must be positive safe integers; processor counts must also fit
|
||||
the JVM's signed 32-bit range. REA preserves
|
||||
`JAVA_TOOL_OPTIONS`, `JDK_JAVA_OPTIONS` and `_JAVA_OPTIONS` rather than replacing
|
||||
them. Java's standard precedence applies: explicit command-line heap settings
|
||||
override `JAVA_TOOL_OPTIONS`, while `_JAVA_OPTIONS` can override command-line
|
||||
settings. Evidence reports the worker's observed `Runtime.maxMemory()` rounded
|
||||
down to MiB and retains exact bytes, Java version and visible processors in its
|
||||
raw runtime response. The observed allocatable heap can differ from configured
|
||||
`-Xmx`, especially with Serial GC. Heap is not a total RSS cap: the JVM also
|
||||
uses native memory. Independent REA instances have independent queues. Whole
|
||||
operations have a **120-second** execution deadline after reaching the queue's
|
||||
front; upstream decompilation is limited to **90 seconds**.
|
||||
|
||||
Decoded manifest and method text have a **1 MiB** upstream byte budget and report
|
||||
truncation. Class-name pages use indexed slices of up to 4,096 names without changing the
|
||||
complete, case-sensitive search contract. A protocol frame above **8 MiB**, or combined stdout/stderr above
|
||||
**32 MiB per operation**, fails with a diagnostic instead of returning an incomplete inventory
|
||||
as complete. These are memory-safety boundaries, not undocumented result caps.
|
||||
|
||||
## Real verification
|
||||
|
||||
Download the explicit public fixtures once; this installs no tools:
|
||||
|
||||
```sh
|
||||
npm run fixtures:android
|
||||
export REA_JADX_MCP_JAR="$PWD/_reference/apk-integration/jadx-headless-mcp-0.7.1-all.jar"
|
||||
export REA_ANDROID_TEST_APK="$PWD/_reference/apk-integration/ApiDemos-debug.apk"
|
||||
npm run verify:android
|
||||
```
|
||||
|
||||
The lane also uses the selected full JDK's `jlink` to create a disposable JRE
|
||||
without the compiler module. It checks real Java startup failures, missing
|
||||
selectors, recovery after failures, and CLI cancellation with owned cleanup.
|
||||
The original APK and host Java configuration remain unchanged.
|
||||
|
||||
The same lane runs on Windows x64 with the bundled native controls and a full
|
||||
JDK. It checks CLI/MCP results, real MCP cancellation and disconnect, and Java
|
||||
exit after abrupt CLI owner termination. Windows forced termination is checked
|
||||
separately from the POSIX CLI SIGTERM handler; it does not claim a structured
|
||||
cancellation result or workspace removal after the owner is forcibly killed.
|
||||
|
||||
The download script verifies fixed SHA-256 values, reuses matching files and
|
||||
refuses to overwrite a different existing file. APK/JAR files live under ignored
|
||||
`_reference/` and are excluded from package contents. The lane exercises actual
|
||||
REA CLI and MCP results with the real engine, separately from synthetic protocol
|
||||
regressions. It needs no Gradle build, emulator, Android SDK or Ghidra.
|
||||
@@ -1,18 +1,218 @@
|
||||
# Apple Application Inventory Projection
|
||||
|
||||
REA can project one or more authenticated `inventory_artifact` Evidence records
|
||||
for the same IPA into an Apple application inventory. The projection is
|
||||
execution-free: it reports exact paths, content hashes, detected formats,
|
||||
runtime-family hints, and path-based bridge hypotheses. It does not parse plist
|
||||
or CMS semantics or claim observed runtime calls.
|
||||
for the same IPA, macOS `.app` directory, ZIP, or DMG into an Apple application
|
||||
inventory. The projection is execution-free: it reports exact paths, content
|
||||
hashes, detected formats, bundle anatomy, runtime-family hints, and path-based
|
||||
bridge hypotheses. It does not parse plist or CMS semantics or claim observed
|
||||
runtime calls.
|
||||
|
||||
The result includes every component in each inventory category and every
|
||||
JavaScript-to-native bridge candidate pair. It has no caller-selected component
|
||||
budget, prefix truncation, or omitted-count fields. Duplicate inventory pages
|
||||
are merged by artifact identity and occurrence path; their Evidence IDs remain
|
||||
attached as source Evidence.
|
||||
```sh
|
||||
rea project-apple-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project_apple_application_graph",
|
||||
"arguments": {
|
||||
"inventory_evidence": ["<inventory_artifact Evidence>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The result includes every component in each inventory category. Bridge
|
||||
hypotheses are the path-based JavaScript/native pairs within each application
|
||||
root. To keep their Cartesian expansion bounded, the projection limits the
|
||||
serialized candidate array to 2 MiB. It computes the pair count and
|
||||
exact UTF-8 JSON size before creating candidate objects, then retains a
|
||||
deterministic prefix when the byte budget is exceeded. The result's
|
||||
`bridge_candidate_coverage` reports total, emitted, and omitted pair counts;
|
||||
overall coverage becomes `partial` and a limitation explains the omission.
|
||||
Duplicate inventory pages are merged by artifact identity and occurrence path;
|
||||
their Evidence IDs remain attached as source Evidence.
|
||||
|
||||
Content nodes identify bytes by SHA-256 and report only content-derived formats.
|
||||
Each occurrence reports its own `artifact_kind`, `artifact_format`, and executable
|
||||
permission. Identical bytes can therefore appear as a script, resource, or native
|
||||
addon at different paths without overwriting another occurrence's role. Package
|
||||
formats such as APK and IPA belong to the occurrence; their ZIP bytes share a
|
||||
content format. Graph and directory identities use locale-independent Unicode
|
||||
ordering.
|
||||
|
||||
For IPA inventories, archive components outside every application root remain
|
||||
in the component lists. Their JavaScript and native candidates are grouped with
|
||||
other unrooted components, preserving the inventory projection's path pairing
|
||||
semantics without attributing them to an application bundle.
|
||||
|
||||
Coverage is `complete-within-inventory` when the supplied pages reconstruct the
|
||||
complete authenticated inventory, and `partial` otherwise. A partial result
|
||||
states that absence is unknown. Component and bridge-candidate arrays are still
|
||||
the complete projection of the inventory that was supplied.
|
||||
states that absence is unknown. Component arrays remain complete when candidate
|
||||
hypotheses are omitted; component arrays still include every component from
|
||||
the supplied inventory pages, and overall coverage is `partial` in that case.
|
||||
|
||||
## Application roots
|
||||
|
||||
| Inventory root | Application root |
|
||||
| -------------------------------------- | --------------------------------------------------- |
|
||||
| IPA | Each `Payload/<Name>.app` |
|
||||
| `.app` directory | `.`, the inventoried bundle itself |
|
||||
| Directory, ZIP, or DMG containing apps | Each outermost `<Name>.app` followed by `Contents/` |
|
||||
|
||||
ZIP archives made with `ditto -c -k --keepParent` report `<Name>.app`. A DMG
|
||||
reports `<image>.dmg/<Volume>/<Name>.app`; DMG child inventory needs the native
|
||||
macOS mounting adapter. When a DMG has only its root identity, the projection is
|
||||
`partial` and says that bundle absence is unknown. Other inventory formats, such
|
||||
as APK or ASAR, are rejected as input errors.
|
||||
|
||||
An inventoried directory whose name ends in `.app` is its own root even when its
|
||||
inventory is partial or lacks `Contents/`. For directory, ZIP, and DMG
|
||||
inventories, components outside every application root are not attributed to
|
||||
the application. This covers installers, disk-image extras, and AppleDouble
|
||||
sidecars, which therefore stay out of runtime families and bridge candidates. A
|
||||
limitation reports how many inventoried entries were left out. IPA projection
|
||||
still lists every archive component, including `SwiftSupport/`.
|
||||
|
||||
`platforms` lists `macos` for application roots with `Contents/`. It lists `ios`
|
||||
for shallow application roots that have an `Info.plist`. An application root
|
||||
with neither, such as an empty inventory, has no platform.
|
||||
|
||||
## macOS bundle anatomy
|
||||
|
||||
`bundles` lists each application root and every nested bundle under it. Each
|
||||
bundle reports its `parent_path`, `layout`, `info_plist_path`,
|
||||
`executable_candidates`, and `signing_paths`.
|
||||
|
||||
| Layout | Meaning | Content directory |
|
||||
| --------------------- | --------------- | --------------------------------------------- |
|
||||
| `macos-deep` | Has `Contents/` | `Contents/`, executables in `Contents/MacOS/` |
|
||||
| `versioned-framework` | Has `Versions/` | Each real `Versions/<version>/` |
|
||||
| `shallow` | Neither | The bundle directory itself |
|
||||
|
||||
Roles are path conventions (`role_basis: "path-convention"`):
|
||||
|
||||
| Role | Path |
|
||||
| -------------------- | --------------------------------------------------------------- |
|
||||
| `app-extension` | `*.appex` |
|
||||
| `xpc-service` | `*.xpc`, including XPC services inside frameworks |
|
||||
| `framework` | `*.framework` |
|
||||
| `system-extension` | `*.systemextension` |
|
||||
| `driver-extension` | `*.dext` |
|
||||
| `plug-in` | `PlugIns/*.bundle`, `*.plugin`, `*.qlgenerator`, `*.mdimporter` |
|
||||
| `resource-bundle` | `Resources/*.bundle`, such as SwiftPM resource bundles |
|
||||
| `bundle` | Any other `*.bundle` |
|
||||
| `login-item` | `Contents/Library/LoginItems/*.app` |
|
||||
| `helper-application` | `Contents/Helpers/*.app` |
|
||||
| `nested-application` | Any other nested `*.app` |
|
||||
|
||||
The components also list:
|
||||
|
||||
- `privileged_helpers`: files directly in `Contents/Library/LaunchServices/`,
|
||||
where SMJobBless helpers live;
|
||||
- `launchd_plists`: `.plist` files directly in `Contents/Library/LaunchAgents/`
|
||||
(`domain: "agent"`) or `Contents/Library/LaunchDaemons/` (`domain: "daemon"`),
|
||||
as used by SMAppService;
|
||||
- `helpers`: files directly in `Contents/Helpers/`.
|
||||
|
||||
Symlinks are reported by path in `symlinks`. Their targets are not inventoried,
|
||||
so a versioned framework is described through its real `Versions/<version>/`
|
||||
directories rather than `Versions/Current`. A framework's `info_plist_path` is
|
||||
`null`, with a limitation explaining why, when it has more than one real
|
||||
`Versions/<version>/` directory (even if only one of them holds a plist,
|
||||
because `Versions/Current` decides which one applies) or when its single
|
||||
version directory has no `Resources/Info.plist`.
|
||||
|
||||
AppleDouble sidecar files (`._*` and `__MACOSX/`) are inventory facts, but they
|
||||
describe neighbouring files. They are excluded from roots, bundle roles, and the
|
||||
anatomy components.
|
||||
|
||||
The projection does not read plists. Use `inspect_plist` on a bundle's
|
||||
`info_plist_path` or a launchd plist to read `CFBundleExecutable`, identifiers,
|
||||
`SMPrivilegedExecutables`, `BundleProgram`, URL schemes, and usage descriptions.
|
||||
Use `inspect_signature` on an executable candidate for code-signing claims.
|
||||
|
||||
## Dylib load resolution
|
||||
|
||||
`trace_dylib_resolution` answers which file each Mach-O dylib load reaches. It
|
||||
runs on the active Mach-O, or on every executable in the active `.app`. Each
|
||||
executable in a bundle (the main app, XPC services, app extensions, login items,
|
||||
privileged helpers, and helper tools) is a separate process root with its own
|
||||
`@executable_path`.
|
||||
|
||||
```sh
|
||||
rea trace-dylib-resolution MyApp.app --json
|
||||
rea trace-dylib-resolution MyApp.app --root Contents/MacOS/MyApp --architecture arm64 --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "trace_dylib_resolution",
|
||||
"arguments": { "roots": ["Contents/MacOS/MyApp"], "architecture": "arm64" }
|
||||
}
|
||||
```
|
||||
|
||||
Load commands are parsed in TypeScript from Mach-O and FAT headers, including
|
||||
the `dylib_use_command` encoding newer linkers emit for flags such as delayed
|
||||
initialization. Nothing is executed and no Apple tool is required, so the tool
|
||||
also works off macOS.
|
||||
|
||||
Resolution follows dyld:
|
||||
|
||||
- `@executable_path` is the directory of the process root's executable. It is
|
||||
undetermined when the root is a library.
|
||||
- `@loader_path` is the directory of the image that holds the load command, or
|
||||
of the image that owns the `LC_RPATH` entry being expanded.
|
||||
- `@rpath` tries each `LC_RPATH` of the loading image first, then those of the
|
||||
image that loaded it, up to the process root.
|
||||
- Dependencies load dependents-first in load-command order, and each image
|
||||
loads once per process. A later request whose install name matches an
|
||||
already loaded image reuses it.
|
||||
- Symlinks such as `Versions/Current` are followed inside the analyzed root.
|
||||
An escape through `..` or an absolute link is `escapes-target`.
|
||||
|
||||
Each edge lists every candidate path tried, with its outcome: `resolved`,
|
||||
`absent`, `not-mach-o`, `malformed`, `unsupported` (recognized Mach-O outside
|
||||
parser coverage, such as big-endian images), `architecture-missing`,
|
||||
`outside-target`, `escapes-target`, or `undetermined`. Slices follow dyld's
|
||||
graded architectures, so an `x86_64h` process also loads `x86_64`. The edge's `resolution` is one of:
|
||||
|
||||
| Status | Meaning |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
|
||||
| `conditional` | An image inside the root resolves, but an earlier candidate outside the root (for example `/usr/lib/swift`) would win if it exists on the running system. |
|
||||
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
|
||||
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
|
||||
|
||||
A `conditional` image is still traced, because it is the image the analyzed
|
||||
root supplies. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. Findings on those
|
||||
edges say that they apply only if the conditional image loads.
|
||||
|
||||
Absolute install names such as `/usr/lib/libSystem.B.dylib` are
|
||||
`outside-target`. On macOS 11 and later most of them live in the dyld shared
|
||||
cache rather than on disk, so REA does not check them against the host.
|
||||
|
||||
`findings` are derived, not observed:
|
||||
|
||||
- `required-load-unresolved`, `weak-load-unresolved`, and `lazy-load-unresolved`.
|
||||
`LC_LAZY_LOAD_DYLIB` dependencies are resolved but not traversed, because dyld
|
||||
loads them on first use; an unresolved one is not a launch failure.
|
||||
- `earlier-rpath-candidate-absent`: a compatible Mach-O placed at an earlier
|
||||
modeled search path could take precedence, subject to unmodeled search inputs
|
||||
and code-signing library validation. These findings do not establish which
|
||||
image dyld will load; use `inspect_signature` for signing metadata.
|
||||
- `dyld-environment-present`: preserves observed `LC_DYLD_ENVIRONMENT` entries.
|
||||
Only an executable root supplies process settings; entries on library and
|
||||
other non-executable roots do not alter resolution or coverage. Applicable
|
||||
executable image-selection overrides are not modeled.
|
||||
|
||||
Empty dyld settings have different consumers. Modern dyld appends a slash and
|
||||
image name to ordinary and fallback directory entries, so an empty entry can
|
||||
search `/child.dylib` or `/Foo.framework/Foo`. REA retains uncertainty for those
|
||||
unchecked paths; an empty fallback does not preempt an ordinary-search hit.
|
||||
Empty versioned-directory lists do not scan a directory, and empty suffixes
|
||||
only repeat the original candidate, so they do not add search uncertainty.
|
||||
Reported command values are retained in all cases.
|
||||
|
||||
`verify:macos-bundle` checks the parser against `otool -l` for every traced
|
||||
image. It also compares the predicted load order of two process roots with the
|
||||
images dyld actually loads (`DYLD_PRINT_LIBRARIES`).
|
||||
|
||||
+78
-12
@@ -1,37 +1,56 @@
|
||||
graph TD
|
||||
subgraph "Entry Points"
|
||||
CLI["src/cli.ts<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
|
||||
MAIN["src/main.ts<br/>MCP stdio server adapter<br/>config, session, process lifetime"]
|
||||
CLI["src/cli.ts + src/cli/<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
|
||||
MAIN["src/main.ts + src/main/<br/>MCP stdio server adapter<br/>config, session, process lifetime"]
|
||||
DISPATCH["scripts/rea.mjs<br/>Executable dispatcher<br/>routes mcp / one-shot"]
|
||||
end
|
||||
|
||||
subgraph "Production Composition"
|
||||
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading"]
|
||||
end
|
||||
|
||||
subgraph "Adapters"
|
||||
SERVER["src/server/<br/>MCP request translation<br/>createServer, tool registration, toolResult"]
|
||||
HOPPER_BRIDGE["bridge/hopper_bridge.py<br/>Runs inside Hopper<br/>adapts operations to Hopper Python API"]
|
||||
GHIDRA_BRIDGE["bridge/ghidra/ReaGhidraBridge.java<br/>Packaged HeadlessScript<br/>inventory, decompiler, references, CFG"]
|
||||
INSTALL["Host installation adapters<br/>macOS: verified vendor DMG to ~/Applications<br/>Linux: verified vendor package + apt/dnf/pacman"]
|
||||
INSTALL["src/cli/ + src/application/Setup*<br/>Host installation adapters<br/>macOS: verified vendor DMG to ~/Applications<br/>Linux: verified vendor package + apt/dnf/pacman"]
|
||||
end
|
||||
|
||||
subgraph "Application Layer"
|
||||
APP["src/application/<br/>Shared CLI/MCP workflows<br/>enhanced analysis and investigations"]
|
||||
SESSION["SessionProviderRouter + BinarySession<br/>one immutable deep binding per target<br/>disjoint auxiliary routing"]
|
||||
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback"]
|
||||
JS_RECON["JavaScript artifact reconstruction<br/>directory/ASAR reads<br/>AST-only bundle projection"]
|
||||
RECORDS["src/application/investigation/<br/>composed InvestigationRecords + narrow ports<br/>EvidenceLedger and Unknown ownership"]
|
||||
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots<br/>compatibility facade and post-commit notifications"]
|
||||
JS_RECON["src/application/javascript/ + src/artifacts/javascript/<br/>JavaScript artifact reconstruction<br/>directory/ASAR reads<br/>AST-only bundle projection"]
|
||||
end
|
||||
|
||||
subgraph "Provider Layers"
|
||||
PWNTOOLS["src/native/pwntools/ + src/native/pwndbg/<br/>BYO offline ELF/core adapters<br/>owned snapshots/Python and optional core-only GDB<br/>unchanged upstream parsers, ABI structures and mapping API"]
|
||||
EVM["src/evm/<br/>unchanged EVMole/WASM in bounded owned worker<br/>explicit carrier parsing and inferred interface adapter"]
|
||||
HOPPER["src/hopper/<br/>Hopper launch & Unix-socket protocol<br/>BridgeLauncher, HopperClient, protocol"]
|
||||
GHIDRA["src/ghidra/<br/>Ghidra discovery, profile & function analysis<br/>snapshot, Unix/Windows transport, bounded lifecycle"]
|
||||
AUX["src/artifacts/ + src/native/<br/>disjoint artifact and native capabilities"]
|
||||
BROWSER["src/browser/<br/>Loopback CDP transport<br/>exact-origin passive observation"]
|
||||
IDA["src/ida/<br/>Upstream MCP contract adaptation<br/>live GUI binding or owned headless database"]
|
||||
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
|
||||
AUX["src/artifacts/ + src/native/<br/>artifact inventory and extraction<br/>format readers and native capabilities"]
|
||||
BROWSER["src/browser/<br/>CDP / Playwright capture<br/>captured scripts and native module resolution<br/>execution/: coverage, listener and source attribution"]
|
||||
JS_CODEC["src/javascript/sourceMaps/<br/>pinned upstream map codec<br/>owned heap-limited Node process, complete point evidence"]
|
||||
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
|
||||
DOTNET["src/dotnet/<br/>Managed static metadata provider<br/>PE/CLI metadata inventory, members, IL"]
|
||||
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
|
||||
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
|
||||
REFERENCE["src/reference/<br/>Filesystem reference-source reader<br/>bounded traversal, validation, errors"]
|
||||
end
|
||||
|
||||
subgraph "Provider Process Foundation"
|
||||
PROCESS["src/process/<br/>owned process groups, private roots<br/>deadlines, bounded file reads and diagnostics, cleanup"]
|
||||
WINDOWS["src/windows/<br/>Windows host boundary adapters<br/>package loader, opaque handles, owned jobs and private runtimes"]
|
||||
WINDOWS_NATIVE["native/windows/src/<br/>Node-API implementation<br/>NTFS admission, protected DACLs and atomic Job Object assignment"]
|
||||
end
|
||||
|
||||
subgraph "Contracts Layer"
|
||||
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, managed, browser, Electron, application, and session tools"]
|
||||
ERRORS["src/contracts/errorSchemas.ts<br/>Shared analysis error schemas<br/>CLI diagnostics, generated error catalog"]
|
||||
end
|
||||
|
||||
subgraph "Domain Layer"
|
||||
@@ -39,48 +58,95 @@ graph TD
|
||||
end
|
||||
|
||||
subgraph "Configuration"
|
||||
CONFIG["src/config.ts<br/>Zod-validated env parsing<br/>provider settings and origins"]
|
||||
CONFIG["src/config.ts + src/config/<br/>Zod-validated env parsing<br/>provider settings and origins"]
|
||||
end
|
||||
|
||||
subgraph "Tests"
|
||||
TESTS["tests/<br/>Vitest suite<br/>fixtures/ holds fake launcher, bridge, and CDP seams"]
|
||||
end
|
||||
|
||||
subgraph "Evaluation"
|
||||
EVALUATION["src/evaluation/<br/>Agent release evaluation<br/>session transcript scoring"]
|
||||
end
|
||||
|
||||
DISPATCH --> CLI
|
||||
DISPATCH --> MAIN
|
||||
MAIN --> CONFIG
|
||||
MAIN --> SERVER
|
||||
MAIN --> APP
|
||||
CLI --> APP
|
||||
MAIN --> COMPOSITION
|
||||
CLI --> COMPOSITION
|
||||
SERVER --> COMPOSITION
|
||||
COMPOSITION --> SESSION
|
||||
APP --> FIRMWARE
|
||||
FIRMWARE --> PROCESS
|
||||
FIRMWARE --> CONTRACTS
|
||||
APP --> ANDROID
|
||||
ANDROID --> PROCESS
|
||||
ANDROID --> CONTRACTS
|
||||
ANDROID --> DOMAIN
|
||||
CLI --> ERRORS
|
||||
APP --> SESSION
|
||||
SESSION --> REGISTRY
|
||||
REGISTRY --> HOPPER
|
||||
REGISTRY --> GHIDRA
|
||||
SESSION --> SNAPSHOT
|
||||
SNAPSHOT --> RECORDS
|
||||
APP -->|record callbacks| RECORDS
|
||||
SERVER -->|narrow record ports| RECORDS
|
||||
RECORDS --> DOMAIN
|
||||
COMPOSITION --> PWNTOOLS
|
||||
PWNTOOLS --> PROCESS
|
||||
PWNTOOLS --> DOMAIN
|
||||
COMPOSITION --> HOPPER
|
||||
COMPOSITION --> GHIDRA
|
||||
COMPOSITION --> IDA
|
||||
COMPOSITION --> AUX
|
||||
IDA --> IDA_MCP
|
||||
IDA --> CONTRACTS
|
||||
IDA --> DOMAIN
|
||||
SESSION --> AUX
|
||||
APP --> JS_RECON
|
||||
APP --> INSTALL
|
||||
SERVER --> CONTRACTS
|
||||
SERVER --> APP
|
||||
APP --> BROWSER
|
||||
APP --> REPLAY
|
||||
APP -->|source-map codec port| JS_CODEC
|
||||
JS_CODEC --> PROCESS
|
||||
JS_CODEC --> DOMAIN
|
||||
APP -->|runtime observation port| INSPECTOR
|
||||
INSPECTOR -->|shared CDP and file-location helpers| BROWSER
|
||||
INSPECTOR --> DOMAIN
|
||||
APP --> DOTNET
|
||||
DOTNET --> DOMAIN
|
||||
HOPPER --> HOPPER_BRIDGE
|
||||
GHIDRA --> GHIDRA_BRIDGE
|
||||
HOPPER --> PROCESS
|
||||
GHIDRA --> PROCESS
|
||||
GHIDRA --> WINDOWS
|
||||
PROCESS --> WINDOWS
|
||||
WINDOWS --> WINDOWS_NATIVE
|
||||
APP --> PROCESS
|
||||
APP --> DOMAIN
|
||||
JS_RECON --> AUX
|
||||
JS_RECON --> DOMAIN
|
||||
APP --> REFERENCE
|
||||
REFERENCE --> DOMAIN
|
||||
CONTRACTS --> DOMAIN
|
||||
HOPPER --> DOMAIN
|
||||
GHIDRA --> DOMAIN
|
||||
AUX --> DOMAIN
|
||||
BROWSER --> DOMAIN
|
||||
REPLAY --> DOMAIN
|
||||
PROCESS --> DOMAIN
|
||||
TESTS -.-> MAIN
|
||||
TESTS -.-> HOPPER
|
||||
TESTS -.-> GHIDRA
|
||||
TESTS -.-> IDA
|
||||
TESTS -.-> PROCESS
|
||||
TESTS -.-> BROWSER
|
||||
TESTS -.-> REPLAY
|
||||
TESTS -.-> EVALUATION
|
||||
TESTS -.-> ERRORS
|
||||
|
||||
COMPOSITION --> EVM
|
||||
APP -->|EVM interface port| EVM
|
||||
EVM --> PROCESS
|
||||
EVM --> DOMAIN
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
# Offline binary diagnostics
|
||||
|
||||
`inspect_binary_layout` / `inspect-binary-layout` answers what an explicit
|
||||
file contains: section/segment layout, original symbols/relocations, static
|
||||
linkage names and mitigation indicators. It works without an active Hopper,
|
||||
Ghidra or IDA target. CTF is one possible use; crash triage, compatibility and
|
||||
ordinary binary inspection use the same modular capability.
|
||||
|
||||
## Bring your own engine
|
||||
|
||||
Initial real verification covers **Linux x64, ELF64 x86-64 little-endian
|
||||
EXEC/DYN/REL**. Provide an absolute Python executable whose environment already
|
||||
contains unchanged pwntools 4.15.0, pyelftools 0.33 and Unicorn 2.1.2:
|
||||
|
||||
```sh
|
||||
REA_PWNTOOLS_PYTHON=/absolute/isolated-env/bin/python \
|
||||
rea inspect-binary-layout ./selected.elf --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_binary_layout",
|
||||
"arguments": { "path": "/artifacts/selected.elf" }
|
||||
}
|
||||
```
|
||||
|
||||
REA never installs Python, packages or GDB, changes a user init file, launches
|
||||
the selected object as a host process, or requests runtime library resolution. The Python process
|
||||
uses isolated mode and an owned cache. Exact upstream profiles are recorded in
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/pwntools/README.md).
|
||||
|
||||
## Interpreting results
|
||||
|
||||
- Artifact path, SHA-256 and size identify original selected bytes. All parsing
|
||||
reads an owned stable snapshot; the original is unchanged.
|
||||
- Addresses, offsets, lengths and flags are hexadecimal strings. A linked
|
||||
address is not a runtime address. Runtime load base remains null. A zero
|
||||
EXEC/DYN entry means absent; a relocatable entry is not applicable.
|
||||
- Each symbol retains its table and entry index. Its reported value can mean
|
||||
undefined, alignment, absolute value, no address, unknown section index,
|
||||
section offset, TLS offset or linked virtual address. Duplicate names remain
|
||||
separate entries. SHN_XINDEX remains unresolved in the selected upstream
|
||||
representation; its external index table is not silently treated as resolved.
|
||||
- Relocatable objects have section-relative relocations and no executable entry
|
||||
claim. Signed REL/RELA relocation addends are decimal strings. Packed RELR
|
||||
tables retain full original bytes and upstream-decoded offsets as derived
|
||||
evidence, with per-offset packed-word locations and implicit addends unknown.
|
||||
Overall relocation inventory completeness remains unknown.
|
||||
A relocatable SHN_UNDEF target or inactive SHT_NULL header remains an explicit
|
||||
unknown with the original reported section index and offset.
|
||||
- REL/RELA symbol references retain their original table and entry indices.
|
||||
Positive indices resolve through a validated symbol table; symbol index zero
|
||||
means a zero symbol value without a table lookup, including when no table is
|
||||
linked. Malformed references fail with the affected section and symbol index.
|
||||
- Section-name and symbol tables require declared SHT_STRTAB links.
|
||||
SHN_UNDEF explicitly means no section names: display is empty, raw name and
|
||||
location unknown, with the original name offsets retained. Extended section-name
|
||||
indices resolve through section zero and undergo the same validation.
|
||||
- Symbol tables require a declared SHT_STRTAB link. Name offsets and terminators
|
||||
must stay inside the declared string table; malformed references fail before
|
||||
unrelated bytes can become names. Dynamic dependencies use DT_STRTAB/DT_STRSZ
|
||||
with a unique file-backed mapping; ambiguous or unbacked mappings are
|
||||
explicitly unsupported.
|
||||
- Name display strings use UTF-8 replacement for opaque bytes, including
|
||||
sectionless dependency names and interpreter paths. Raw name
|
||||
bytes and string-table ranges retain observed identity where resolvable;
|
||||
ranges include the terminating NUL and base64 bytes exclude it.
|
||||
- NOBITS and NULL sections provide no file bytes. Reported file-backed ranges
|
||||
are checked against snapshot size again at the public boundary. PT_NULL payload
|
||||
fields are unused: reported numbers remain, file backing is none and interpreted
|
||||
permissions are null. Segments with zero file size also have no file bytes.
|
||||
- Sectionless dynamic images still report dependency names with raw bytes and
|
||||
file locations where uniquely mapped. Missing symbol/relocation section tables
|
||||
do not establish that those runtime tables are absent.
|
||||
Upstream RELRO/canary heuristics rely on sections and can be incomplete in
|
||||
this case; their reported candidates retain an explicit coverage limitation.
|
||||
- DT_NEEDED/PT_INTERP are reported names, not resolved runtime paths. GOT/PLT
|
||||
maps are derived convenience views and may collapse aliases. PLT inference
|
||||
can emulate selected instructions in Unicorn; its outputs are static candidates.
|
||||
Exec syscall tracing observes host process launches. Map completeness
|
||||
remains unknown and original upstream warnings are returned inline.
|
||||
- Canary, PIE, NX, executable-stack and RELRO are static inferences. NX and
|
||||
executable-stack are separate upstream indicators: on x86-64 an executable
|
||||
stack can accompany an unknown (`null`) NX indicator. ET_DYN can be a shared
|
||||
library, and an absent canary symbol does not prove every function unprotected.
|
||||
|
||||
When complete layout Evidence is already retained, `inspect_analysis_view` /
|
||||
`inspect-analysis-view` projects a summary, the mitigations or linkage facet,
|
||||
one section or symbol, or a stable page without re-running the decoder. CLI
|
||||
one-shot invocations pass portable inline Evidence JSON; an MCP session uses
|
||||
the retained `evidence_id`. Page `limit` is required and bounded by the
|
||||
measured MCP stdio budget documented on the tool contract.
|
||||
|
||||
Complete results have a 32 MiB input, 64 MiB reply, 1 MiB combined diagnostics
|
||||
and 30-second owned command deadline. The reply budget applies to the decoder
|
||||
record; the CLI/MCP Evidence envelope adds copies and encoding overhead. The
|
||||
pinned MCP SDK defaults to a 10 MiB receive buffer. Large complete results need
|
||||
a caller-configured `StdioClientTransport({ maxBufferSize: ... })`; the large
|
||||
extended-index verification uses 256 MiB and an explicit five-minute request
|
||||
timeout in its separate opt-in MCP lane. REA does not silently truncate the result. Python lowers resource soft limits to at most 3 GiB virtual address space,
|
||||
30 CPU seconds and 64 MiB file output. Inherited tighter soft/hard limits are
|
||||
retained, and the effective values are returned in the result limitations. Virtual address space is not RSS;
|
||||
Unicorn needs a large virtual map. Limits fail with no partial success, and
|
||||
owned process/root cleanup completes independently of cancellation.
|
||||
|
||||
Core files, GDB session/state/control, optional pwndbg enrichment and instruction
|
||||
inspection are separate increments tracked by #969. This increment makes no
|
||||
real support claim for those features or other operating systems/architectures.
|
||||
|
||||
Typed decoder failures retain bounded `captured_output` (stdout, stderr and an
|
||||
explicit truncation flag) alongside the original failure category and reason.
|
||||
Successful `diagnostics` also exposes the supervisor's `truncated` flag. Output
|
||||
beyond the complete diagnostic budget is rejected, including when the decoder
|
||||
writes a valid reply; cleanup and late cancellation reuse the observed flag.
|
||||
Memory allocation failures use `resource_constraint`, with effective resource
|
||||
limits (or an explicit unknown) and memory-specific recovery guidance. A
|
||||
MemoryError alone does not establish the exact failed allocation or that a
|
||||
particular budget was exhausted. Invalid undersized symbol entries are rejected
|
||||
before REA reports source ranges for them.
|
||||
The bridge reserves 1 MiB to report allocation failures. If even error reporting
|
||||
or serialization fails with MemoryError, its reserved exit status preserves the
|
||||
resource classification with unknown effective limits.
|
||||
Reserved exits require a matching private marker written by the bridge's actual
|
||||
failure branch. Bare launcher exits or missing/unwritable markers retain process
|
||||
diagnostics with the resource cause unverified.
|
||||
Signal termination alone is never classified as observed memory exhaustion.
|
||||
The small bootstrap also covers catchable MemoryError during adapter imports,
|
||||
compilation and initialization. Python interpreter startup failures before the
|
||||
bootstrap runs retain their observed process diagnostics without guessed causes.
|
||||
SIGXCPU retains a CPU resource diagnostic and CPU-specific recovery advice.
|
||||
Observed EFBIG writes or SIGXFSZ termination retain a file-size resource
|
||||
diagnostic and file-size-specific recovery advice. If a tight file limit also
|
||||
prevents writing the limit record, effective values remain unknown.
|
||||
The bridge records actual limits in owned storage before analysis; missing or
|
||||
malformed limit reports remain unknown with their read failure preserved. A
|
||||
received signal alone does not establish its exact cause. Dynamic tags require
|
||||
a complete DT_NULL inside PT_DYNAMIC; interpreter names and ranges end at the
|
||||
first NUL, with any padding still represented by the original segment range.
|
||||
+34
-12
@@ -1,14 +1,20 @@
|
||||
# Website observation with CDP
|
||||
|
||||
Captured Debugger sources can be exported for REA's existing static JavaScript
|
||||
analysis with `export_web_scripts`. See [captured website scripts](website-script-export.md)
|
||||
for the capture options, source mappings, and browser/local resolution limits.
|
||||
Use [captured module relationships](website-module-trace.md) to resolve one
|
||||
exported script's native imports under an explicit URL/import-map context.
|
||||
|
||||
REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.
|
||||
|
||||
## Shipped surfaces
|
||||
|
||||
- `list_browser_targets` / `rea list-browser-targets` discovers page targets at the selected target's current origin by default, with an optional exact-origin filter.
|
||||
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
|
||||
- `inspect_web_page` / `rea inspect-web-page` captures DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally selected storage key names, redacted storage-content fingerprints, or script sources.
|
||||
- `analyze_web_bundle` / `rea analyze-web-bundle` parses selected script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally source-map/original-source evidence.
|
||||
- `observe_web_session` / `rea observe-web-session` arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
|
||||
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete structural input-schema summary inline. REA never exposes `WebMCP.invokeTool`; malformed protocol payloads fail explicitly instead of returning incomplete tool metadata.
|
||||
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete structural input-schema summary inline. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. REA never exposes `WebMCP.invokeTool`; malformed protocol payloads fail explicitly instead of returning incomplete tool metadata.
|
||||
- `compare_web_captures` / `rea compare-web-captures` compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
|
||||
- `capture_web_screenshot` / `rea capture-web-screenshot` returns a content-addressed visible-viewport PNG.
|
||||
- `compare_web_screenshots` / `rea compare-web-screenshots` performs bounded local PNG pixel comparison without OCR or external services.
|
||||
@@ -18,6 +24,17 @@ REA can attach to a user-owned Chrome-family browser through the Chrome DevTools
|
||||
Target discovery returns the complete in-scope target array in one result.
|
||||
|
||||
Electron `file://` pages use a separate provider and target boundary; see [electron-observation.md](electron-observation.md).
|
||||
|
||||
## Provider authorities
|
||||
|
||||
CDP page capture, V8 Inspector observation, and Playwright scenarios stay
|
||||
separate providers because their authorities differ: CDP and V8 attach to an
|
||||
already-running target and never launch, drive, evaluate, or mutate it, while
|
||||
Playwright providers own and drive the runtime (launch browsers, run actions,
|
||||
capture step snapshots). Merging them would mix attach-only and owned-process
|
||||
lifecycles in one contract. What they share instead is one exclusion
|
||||
vocabulary (`BrowserExclusionReason`): every denial reason in every stack maps
|
||||
onto it, even where a wire schema keeps a narrower historical bucket.
|
||||
Existing static application Evidence and passive web/Electron captures can be
|
||||
combined later through
|
||||
[JavaScript static/runtime reconciliation](javascript-runtime-reconciliation.md).
|
||||
@@ -37,9 +54,9 @@ REA does not launch, own, or terminate this browser. Use a dedicated profile and
|
||||
|
||||
## Request boundary
|
||||
|
||||
Each request supplies a literal loopback CDP endpoint and target. Target
|
||||
discovery uses the selected target's current origin by default; an optional
|
||||
exact HTTP(S) origin filter can narrow the result. `localhost`, private-LAN
|
||||
Discovery supplies a literal loopback CDP endpoint and returns all eligible
|
||||
page targets unless an exact HTTP(S) origin filter narrows the result. Follow-up
|
||||
inspection requests select one target; their default scope is its current origin. `localhost`, private-LAN
|
||||
addresses, HTTPS CDP endpoints, credentials, paths, queries, fragments, and
|
||||
implicit ports are not valid CDP endpoints. Origin filters contain only scheme,
|
||||
host, and port; paths, credentials, queries, fragments, and wildcards are not
|
||||
@@ -116,12 +133,12 @@ REA retains local observation data and removes only URL userinfo credentials, ex
|
||||
- URL observations preserve the complete local URL, including query values, fragments, duplicate parameters, and their order. Only username/password userinfo is removed; malformed original URL strings remain available with `origin: null`.
|
||||
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.
|
||||
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
|
||||
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
|
||||
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Redirects reported by CDP are attached as ordered `redirects` on the one request ID, with the prior request URL, CDP response URL, method/type, response status/MIME/encoded length, request timestamp, and the next `requestWillBeSent` event timestamp. CDP delivers `redirectResponse` with that next request event, so the latter is an event boundary rather than an exact response timestamp. Redirect response headers and bodies are discarded; hops outside the selected origins are discarded with the request chain. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
|
||||
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free property paths/types, and discarded. `Network.getResponseBody` is never sent unless JSON body shape capture is selected.
|
||||
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
|
||||
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
|
||||
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
|
||||
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
|
||||
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Script metadata admission bounds retained identity and source-map strings to 8 MiB and 50,000 records. When a source-map URL cannot fit, the script identity remains and source-map coverage reports `resource_budget_exhausted`; rejected script observations are counted without retaining their IDs. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
|
||||
|
||||
Frames, resources, scripts, events, and workers outside the selected origin scope are excluded. An explicit `allowed_origins` list can include additional origins; excluded target details are counted without being exposed.
|
||||
Retained workers include validated opener-target and parent-frame IDs when CDP
|
||||
@@ -131,21 +148,26 @@ prove static module ownership.
|
||||
## Completeness and limits
|
||||
|
||||
The default inspection window is 500 ms; callers choose its duration. The
|
||||
capture returns all in-scope frames, DOM and accessibility nodes, scripts,
|
||||
capture returns in-scope frames, DOM and accessibility nodes, admitted scripts,
|
||||
resources, workers, storage names, and observed network, console, and WebSocket
|
||||
events. Full selected text, script sources, and JSON shapes are returned inline.
|
||||
The result distinguishes
|
||||
`complete_within_window`, `policy_filtered`, `attach_limited`, and `truncated`
|
||||
coverage. It reports excluded items and any values omitted or truncated by
|
||||
validation; it does not drop records to meet an internal collection quota.
|
||||
validation and resource bounds. Omitted script metadata makes its affected
|
||||
section explicitly truncated.
|
||||
Disallowed-origin entries are filtered before retention.
|
||||
|
||||
CDP discovery, WebSocket connection, and commands continue until the peer responds or the caller cancels the operation; REA has no internal byte, message-count, or pending-command quota for these exchanges. Without caller cancellation, a stalled peer can leave an operation waiting. On cancellation, REA closes only its own WebSocket, waiting up to one second before terminating it. Malformed unsolicited protocol events poison the connection so later commands fail closed.
|
||||
CDP discovery, WebSocket connection, and commands continue until the peer responds or the caller cancels the operation; REA has no internal byte, message-count, or pending-command quota for these exchanges. Without caller cancellation, a stalled peer can leave an operation waiting. On cancellation, REA closes only its own WebSocket, waiting up to one second before terminating it. Malformed unsolicited events and malformed replies to pending commands poison the connection so later commands fail closed. When a correlated reply reports a session ID, it must match the command's selected session. An optional method is reported unavailable only when the browser rejects it as unknown; other command rejections retain their reported failure.
|
||||
|
||||
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Every requested map, mapping, original source, AST node, and finding from parsed content is returned inline; failed maps remain explicitly unavailable and make bundle completeness partial.
|
||||
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
|
||||
|
||||
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation has a 30-second deadline and a 64 MiB aggregate streamed-response budget across redirects. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
|
||||
|
||||
## Non-goals and threat model
|
||||
|
||||
The browser provider deliberately does not expose generic CDP commands, `Runtime.evaluate`, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and returns an image only when requested. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
|
||||
|
||||
This feature is not a browser sandbox or network containment mechanism. The attached page and browser continue running with their existing privileges and may make external requests independently of REA. CDP gives deep access to the selected browser profile, so use a dedicated profile, include only origins you intend to inspect, and treat other same-user processes as outside this boundary.
|
||||
|
||||
Response metadata normalizes a `Referrer-Policy` fallback list to its last recognized nonempty policy, including mixed-case tokens. Unknown policy names are skipped; malformed token syntax and lists without a recognized policy remain `null`. This reports the response's declared policy, not the effective policy of a document or an inferred browser default.
|
||||
|
||||
@@ -21,6 +21,115 @@ Duration, action, and navigation
|
||||
timeouts remain fixed provider-owned liveness limits; the request does not
|
||||
accept a caller-controlled `limits` object.
|
||||
|
||||
## Minimal launch example
|
||||
|
||||
Save this as `browser-scenario.json`, replacing the executable path with an
|
||||
already-installed Chrome-family browser and the URL with your running fixture:
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{ "step_id": "settle", "action": "wait_for_timeout", "duration_ms": 100 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
rea capture-browser-scenario ./browser-scenario.json --json > browser-capture.json
|
||||
```
|
||||
|
||||
The default capture includes the final URL. Select DOM, screenshots, or events
|
||||
only when they help answer the question. REA launches and cleans up the browser
|
||||
profile for this request; it does not start your fixture server or install a
|
||||
browser. A schema-valid scenario does not prove that the executable or target
|
||||
is available on the host.
|
||||
|
||||
## Interaction example: capture the visible result
|
||||
|
||||
The default final URL answers "where did the page end up?", not "what did the
|
||||
page show?". Many interactions, such as a search button that renders results in
|
||||
place, change the page without changing the URL. A capture with only the
|
||||
default `url` artifact then reports that the click succeeded and the URL is
|
||||
unchanged, which cannot tell you whether results appeared.
|
||||
|
||||
Request the artifacts that answer your question and wait for the state you want
|
||||
to inspect. This scenario clicks a Search button, waits for the current search
|
||||
results, and captures the final DOM, accessibility tree, and URL. It assumes the
|
||||
local page clears `#results`'s `data-state` while loading and sets it to `ready`
|
||||
after rendering the current response. Adapt the selectors and readiness marker
|
||||
to your page; an already-visible container or previous results are insufficient.
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{
|
||||
"step_id": "search",
|
||||
"action": "click",
|
||||
"locator": { "kind": "css", "selector": "#search" },
|
||||
"timeout_ms": 10000
|
||||
},
|
||||
{
|
||||
"step_id": "results-ready",
|
||||
"action": "wait_for",
|
||||
"locator": { "kind": "css", "selector": "#results[data-state='ready']" },
|
||||
"state": "visible",
|
||||
"timeout_ms": 10000
|
||||
}
|
||||
],
|
||||
"capture": { "at_end": ["dom", "accessibility", "url"] }
|
||||
}
|
||||
```
|
||||
|
||||
Save it as `browser-search.json` and run it the same way as the minimal
|
||||
example:
|
||||
|
||||
```bash
|
||||
rea capture-browser-scenario ./browser-search.json --json > browser-search-capture.json
|
||||
```
|
||||
|
||||
Through MCP, pass the same object as the `capture_browser_scenario` arguments.
|
||||
The final step's DOM and accessibility artifacts retain the page state at that
|
||||
capture time inline. REA still launches and cleans up the browser profile.
|
||||
|
||||
The click's `timeout_ms` covers the click action. A non-navigating `fetch` or
|
||||
delayed render can finish later, so use `wait_for` to observe a page-specific
|
||||
ready condition before final capture. A page that updates synchronously can
|
||||
omit that wait. REA does not infer application readiness from capture selection.
|
||||
|
||||
Choose captures by the question being asked:
|
||||
|
||||
- `dom` and `accessibility`: text or structure the page rendered, such as
|
||||
search results, validation messages, or updated lists. The accessibility tree
|
||||
is usually the more compact way to read visible text.
|
||||
- `screenshot`: visual layout or styling, or content not exposed as text, such
|
||||
as a canvas.
|
||||
- `history` or `storage`: navigation history, or cookies and web storage the
|
||||
interaction wrote. Request them only when that state is the question; they
|
||||
are never captured implicitly.
|
||||
- `after_each_step` instead of `at_end`: intermediate state between actions in
|
||||
a multi-step scenario.
|
||||
- `events` (`console`, `page-errors`, `network`, and so on): how the page
|
||||
reached its result, such as the request a search issued or an error it
|
||||
logged. Select only the event families you need.
|
||||
|
||||
Capture states are reported per artifact. An artifact you did not request is
|
||||
`not_requested`, which means REA did not look, not that the page had nothing
|
||||
to show. A step's `completeness` of `complete` covers only the sections that
|
||||
were requested. A URL-only capture can be `complete` and still be unable to
|
||||
answer a question about rendered content, so add the relevant artifact and run
|
||||
the scenario again rather than inferring from its absence. Capture completeness
|
||||
also does not assert that the page's background work has finished.
|
||||
|
||||
The browser boundary is part of the contract. Launch mode requires a
|
||||
caller-selected executable and always uses a provider-owned temporary profile
|
||||
that is closed and deleted during cleanup. Connect mode accepts only an
|
||||
@@ -85,6 +194,83 @@ external browser without closing it. The request itself defines the operation;
|
||||
there is no additional REA grant step. The browser and host still enforce their
|
||||
own access and process rules.
|
||||
|
||||
## Network evidence
|
||||
|
||||
`capture.events: ["network"]` retains request, response, completion, failure,
|
||||
and unfinished-request metadata. Select content independently:
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{
|
||||
"step_id": "run",
|
||||
"action": "click",
|
||||
"locator": { "kind": "css", "selector": "#start" },
|
||||
"timeout_ms": 10000
|
||||
},
|
||||
{
|
||||
"step_id": "ready",
|
||||
"action": "wait_for",
|
||||
"locator": { "kind": "css", "selector": "#done" },
|
||||
"state": "visible",
|
||||
"timeout_ms": 10000
|
||||
}
|
||||
],
|
||||
"capture": {
|
||||
"network": {
|
||||
"request_body": true,
|
||||
"response_body": true,
|
||||
"header_values": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All three options default to `false`. Any selected network content also enables
|
||||
network metadata observation; listing `network` in `events` is then optional.
|
||||
Each observed request object receives a capture-scoped `transaction_id`.
|
||||
Requests sharing a URL remain distinct. Redirects link to the preceding
|
||||
transaction only when it was observed; a null predecessor does not establish
|
||||
that no earlier hop existed. Older captures without IDs remain readable and
|
||||
their transaction association is unknown.
|
||||
|
||||
`scenario.network_content` records the resolved content selection. Comparing
|
||||
captures with different selection, or legacy captures without that coverage
|
||||
information, reports network evidence as unknown with a recovery instruction.
|
||||
Capturing more data alone does not establish a website change.
|
||||
|
||||
`network-content` events reference the exact request or response event through
|
||||
`source_event_sequence`. They include independently typed headers and body
|
||||
states. Captured bodies retain base64 bytes, byte count, media type, and SHA-256
|
||||
of the retained bytes. Request bytes are those exposed by Playwright; a
|
||||
`not_exposed` request body does not prove absence or complete multipart file
|
||||
coverage. Response bytes are decoded by the browser, not the compressed wire
|
||||
representation. No later fetch is substituted for the original response.
|
||||
Headers preserve producer order and duplicates. Authorization,
|
||||
Proxy-Authorization, Cookie, and Set-Cookie values are null with
|
||||
`redacted: true`; ordinary values remain intact. Declared UTF-8 secret values
|
||||
and their standard URI/form or JSON-escaped representations are replaced without
|
||||
decoding unrelated binary bytes. Other encodings are not interpreted. Digests describe the
|
||||
retained representation after redaction.
|
||||
|
||||
Only completed responses are read. At the end of the final step, observation
|
||||
stops; requests still in flight emit `request-unfinished`, and their observed
|
||||
responses have explicitly unavailable bodies. This cutoff does not wait for
|
||||
long-lived streaming connections. Already-started content reads settle before
|
||||
browser cleanup, with a provider-owned five-second timeout per read and
|
||||
cancellation support. Failed or unexposed reads retain per-content states and make selected
|
||||
event coverage incomplete. Body events can arrive during later steps or final
|
||||
drain; event bounds describe receipt during the step, while the source event
|
||||
reference establishes the transaction phase. Comparison attributes content to
|
||||
its source event's step and excludes the sequence reference from normalization.
|
||||
Playwright scenario capture does not supply initiator stacks; passive CDP
|
||||
observation retains its own initiator evidence.
|
||||
|
||||
## Scenario comparison
|
||||
|
||||
`compare_web_captures` and `compare-web-captures` also accept two complete
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
# Incremental capability migration
|
||||
|
||||
This working guide tracks the migration in #740. Update it as concrete domains
|
||||
migrate. Existing public contracts and domain-specific ports remain authoritative;
|
||||
there is no new universal provider interface or fixed module manifest.
|
||||
|
||||
## Ownership to preserve
|
||||
|
||||
| Responsibility | Current owner | Migration direction |
|
||||
| --------------------------------------------- | ----------------------------------- | ----------------------------------------------------- |
|
||||
| Analyst semantics and Evidence representation | domain | Keep pure and provider-neutral |
|
||||
| Named public input/output contracts | contracts | Keep exact CLI/MCP meaning and complete discovery |
|
||||
| Producer parsing and external tool protocols | provider adapters | Keep behind the relevant typed port |
|
||||
| Shared analyst workflows | application | Share between CLI and MCP |
|
||||
| Concrete provider construction | composition, MCP startup | Keep fresh typed factories at the production boundary |
|
||||
| Investigation Evidence and Unknowns | InvestigationRecords/EvidenceLedger | Consume narrow read/write/atomic record ports |
|
||||
| Target/profile snapshots and invalidation | BinarySessionRecords/BinarySession | Keep binary-owned |
|
||||
| Subprocess ownership and host primitives | process/windows | Reuse; preserve cancellation and cleanup |
|
||||
| Public translation | CLI/MCP adapters | Delegate to shared workflows and named contracts |
|
||||
|
||||
Retain exact Evidence IDs, provenance, detached reads, atomic Evidence/Unknown
|
||||
mutations and optimistic revisions. Preserve current close and failed-cleanup
|
||||
record clearing while extracting ownership; #721 owns any retention-policy change.
|
||||
Each CLI invocation and MCP connection keeps its own state. Provider registration
|
||||
must not launch engines or install prerequisites.
|
||||
|
||||
## Migrate before generalizing
|
||||
|
||||
1. Record the actual callers, state, environment, protocol and verification owner
|
||||
of the capability being migrated.
|
||||
2. Move concrete construction behind its existing typed port. Preserve injected
|
||||
ports, selected environment, queues, failure state and cleanup behavior.
|
||||
3. Test both caller paths and the applicable package/real-provider boundary.
|
||||
4. Use independently migrated capabilities to identify repeated metadata and
|
||||
composition needs. Derive a small source declaration only when those needs
|
||||
are established; keep unlike target/lifecycle semantics separate.
|
||||
5. Move a coherent capability's files, fixtures and verification lanes in a
|
||||
mechanical PR. Update all path-sensitive consumers in that same PR.
|
||||
|
||||
Pure helpers do not need an interface each. A capture-format adapter or typed
|
||||
factory may be sufficient. Avoid generic JSON execution, external MCP catalog
|
||||
passthrough, eager provider acquisition, new permission fields or a multi-package
|
||||
rewrite. Optional loading should preserve successful peers and report the failed
|
||||
capability without hiding tools from the catalog.
|
||||
|
||||
## Checks before and after a move
|
||||
|
||||
`npm run verify:test-discovery` compares actual Vitest file discovery with every
|
||||
repository-owned `*.test.ts` under src and tests, including newly added files. The
|
||||
check requires Git, includes untracked non-ignored tests, and excludes removed
|
||||
paths during a move. It
|
||||
rejects missing files and overlapping project ownership. It only lists files;
|
||||
it does not replace behavior tests or real-provider verification.
|
||||
|
||||
Check imports, project isolation and coverage, development-test selection,
|
||||
lint exceptions, catalog/generator imports, npm and CI verification entrypoints,
|
||||
fixture ownership, and installed-package bridge/native path resolution.
|
||||
Domain and contracts must not import application workflows, caller adapters or
|
||||
provider implementations. Production composition is a deliberate outer boundary.
|
||||
Other existing adapter imports will migrate incrementally; do not widen a lint
|
||||
exception to conceal new protocol code inside application workflows.
|
||||
|
||||
`npm run verify:module-boundaries` parses repository-owned source imports,
|
||||
reexports, type imports and literal dynamic imports, then checks their resolved
|
||||
source layer. Nested names such as `domain/android` are domain code; `src/android`
|
||||
is the provider adapter. The check runs with lint, including cached static gates.
|
||||
It covers tracked and untracked non-ignored TypeScript source and skips cached
|
||||
paths removed during a move. Pass exact source paths for a focused check.
|
||||
|
||||
Pure source and its tests keep inward dependencies. Application/server provider
|
||||
construction guards cover the migrated deep, Android, firmware, JavaScript recovery and observation
|
||||
implementations. Application workflows cannot import composition, including the
|
||||
former binary runtime entrypoint; shared browser capture/export helpers remain admitted while their
|
||||
ownership is reviewed. Test lane restrictions still use Oxlint. Producer-dependent
|
||||
browser and managed comparison tests live in boundary lanes with their original
|
||||
fixtures and assertions. This is a development guard, not runtime authorization.
|
||||
|
||||
Select verification by the claim changed: schema/composition, package, real
|
||||
engine or host-native workflow. Record host/target prerequisites and source-owned
|
||||
fixtures. Run heavy lanes serially with bounded workers/heaps and reuse existing
|
||||
engines. Recording ports do not prove real provider or platform support.
|
||||
|
||||
## Extension examples to validate during migration
|
||||
|
||||
An implementation of an existing port should require a provider adapter, typed
|
||||
production factory selection, exact producer parsing/provenance and matching
|
||||
verification. Existing workflow meaning and public contracts should stay stable.
|
||||
|
||||
A new analyst capability deliberately adds semantics, named contracts, a typed
|
||||
port/workflow when needed, both caller adapters, availability, documentation and
|
||||
verification. Its integration is broader than swapping a provider. The migration
|
||||
should eliminate incidental duplicate wiring, not erase those design decisions.
|
||||
|
||||
Update these examples with real migrated entrypoints before closing #740.
|
||||
|
||||
## First production factory migration
|
||||
|
||||
`src/composition/android.ts` and `src/composition/firmware.ts` construct providers
|
||||
behind the existing AndroidAnalysisPort and FirmwareAnalysisPort. CLI registrars
|
||||
pass their selected environment; MCP uses the process environment unless a caller
|
||||
injects a port. Each factory call returns a fresh instance and acquires no process,
|
||||
workspace or toolchain. Queues, cleanup-failure state and per-request cleanup stay
|
||||
inside the provider. The existing launcher seams remain available to boundary
|
||||
fixtures, which now exercise the production factories.
|
||||
|
||||
Application workflows and MCP registration must not import these concrete
|
||||
providers. Composition may import their implementations. Binary composition also
|
||||
uses this outer boundary after its direct-analysis callers were migrated.
|
||||
This concrete pilot does not introduce a shared provider interface or module
|
||||
manifest.
|
||||
|
||||
## Independent optional observation adapters
|
||||
|
||||
Browser observation, browser scenarios, Electron observation, Electron scenarios
|
||||
and V8 Inspector observation now have separate factories in `src/composition/`.
|
||||
CLI commands reuse them; MCP startup dynamically imports each factory independently.
|
||||
`OptionalObservationProviders` describes these five existing typed ports only. It
|
||||
is provisional startup wiring, not an extensible plugin manifest or a universal
|
||||
provider interface.
|
||||
|
||||
A failed import or constructor retains the successful peers. The complete tool
|
||||
catalog remains advertised. Session availability reports the failed adapter and
|
||||
its actual reason, and affected handlers return the existing capability-unavailable
|
||||
error. Explicit loading failures take precedence over configured availability.
|
||||
Passive Electron and active Electron failures are independent; static JavaScript
|
||||
analysis and runtime reconciliation remain usable without either adapter. CLI
|
||||
Electron commands also load only their selected runtime factory at execution.
|
||||
|
||||
Source identities are available without importing the optional implementations.
|
||||
Registration acquires no engines, probes no endpoints, changes no permissions and
|
||||
installs nothing. Core transport failures and shutdown still use their existing
|
||||
lifecycle. Fault-injection coverage uses the production loading/startup path and
|
||||
actual MCP SDK calls; recording ports establish composition behavior, while the
|
||||
real Inspector lane separately establishes runtime behavior.
|
||||
|
||||
## Investigation record ownership pilot
|
||||
|
||||
`src/application/investigation/InvestigationRecords.ts` owns one existing
|
||||
EvidenceLedger per runtime. The production session composition constructs that
|
||||
owner explicitly; the direct BinarySession constructor retains a fresh default
|
||||
for compatibility. There is no second ledger, global store, persistence or new
|
||||
retention policy.
|
||||
|
||||
The ledger, Unknown Evidence helper and the ledger's two original behavior tests
|
||||
now live beside the owner in `src/application/investigation/`. Bundle validation
|
||||
and record-owner consumers follow the new paths; retained-reference access still
|
||||
uses the narrow record ports. The move preserves implementation bodies and test
|
||||
assertions. Binary snapshot/cache files remain with their existing owner.
|
||||
|
||||
EvidenceReader, EvidenceWriter and EvidenceUnknownWriter describe existing
|
||||
read/write/atomic callback needs. UnknownRegistryPort preserves optimistic
|
||||
revisions and consistency verification. Non-binary MCP registrars use these
|
||||
record types without depending on the binary execution/lifecycle surface.
|
||||
BinarySessionPort retains its compatible record methods through composition.
|
||||
|
||||
BinarySessionRecords remains the snapshot owner and compatibility facade. It
|
||||
passes the actual active target explicitly for recordUnknown/updateUnknown;
|
||||
recordEvidenceWithUnknown keeps its target-free mutation subject. Snapshot import
|
||||
merges records without notifying midway, then emits after the cache commits.
|
||||
Bundle import retains the full change delta so Unknown-only changes notify even
|
||||
when recordsAdded is zero. Clearing resets records, cache and invalidation before
|
||||
notification; observer failures remain best effort after a committed mutation.
|
||||
|
||||
Close and failed-cleanup clearing, ordinary target-switch retention, detached
|
||||
reads, Evidence identities and deterministic bundle transfer keep their existing
|
||||
behavior. Snapshots still require an active target and concrete analysis profile;
|
||||
extracting investigation ownership does not make them a target-free record store.
|
||||
#721 remains responsible for any future lifetime-policy change.
|
||||
|
||||
## Inspector adapter ownership
|
||||
|
||||
`src/inspector/` owns V8 Inspector discovery, passive capture, script/target
|
||||
location interpretation and its lightweight provider identity. The composition
|
||||
factory and optional loader use this owner. Browser CDP connection, endpoint/value
|
||||
and Electron file-location helpers remain deliberate shared adapter utilities at
|
||||
their existing paths; no provider wire protocol moves into domain or process.
|
||||
|
||||
Loopback fixtures and producer boundaries now live under
|
||||
`tests/fixtures/inspector/` and `tests/boundary/inspector/`. The socket-backed
|
||||
unresolved-target reconciliation test moves from composition into the forked
|
||||
boundary lane with its original assertions. Adapter tests retain fork isolation.
|
||||
`npm run verify:inspector` keeps its public development entrypoint and delegates
|
||||
to `scripts/verify/inspector/runtime-observation.mjs`, using the real Node fixture
|
||||
under `tests/conformance/inspector/`. Catalog imports, source guards and test
|
||||
discovery follow the new paths. Observation authority, lifecycle, producer
|
||||
interpretation and caller-visible contracts stay unchanged.
|
||||
|
||||
## Auxiliary source declaration pilot
|
||||
|
||||
Artifact, macOS native and managed static metadata now belongs to each adapter's
|
||||
`*ProviderMetadata.ts` module. The implementation and
|
||||
`src/composition/auxiliaryAnalysisProviders.ts` reuse the same identity and exact
|
||||
capability descriptors. The declarations reference existing AnalysisProvider
|
||||
loaders; they cover these three disjoint auxiliary implementations only. Deep
|
||||
candidate selection and the five typed observation ports remain separate.
|
||||
|
||||
The binary runtime reads these source declarations while auxiliary construction
|
||||
stays lazy. The separate managed-only entrypoint retains direct construction for
|
||||
its execution-free workflow. The MCP catalog generator reads declarations without
|
||||
constructing implementations, using its canonical macOS platform; actual runtime
|
||||
host restrictions remain explicit.
|
||||
Discovery and binary operation-kind routing now consume canonical source contracts
|
||||
rather than their generated JSON artifact. No declaration imports its generated
|
||||
output. Generated catalog facts and public schemas stay unchanged.
|
||||
|
||||
All observation registrars resolve contracts by name. An isolated compiled-process
|
||||
test reverses presentation arrays and performs actual MCP SDK calls to verify
|
||||
advertised schemas and handler meaning. Reordering metadata must not select a
|
||||
different operation, schema, provider or deep-provider priority.
|
||||
|
||||
## Android layer and verification ownership
|
||||
|
||||
Android workflows and their existing typed port live in `src/application/android/`,
|
||||
pure APK/inventory semantics in `src/domain/android/`, and the named inspection
|
||||
contracts in `src/contracts/android/`. The headless JADX producer remains in
|
||||
`src/android/`; `src/composition/android.ts` supplies its fresh factory to both
|
||||
caller adapters. Public names, result meaning and JVM/queue/cleanup limits are
|
||||
unchanged. Inventory-only application projection remains execution-free.
|
||||
|
||||
The real lane and signal-cleanup helper belong to `scripts/verify/android/`; its
|
||||
explicit downloader and fixed manifest belong to `scripts/fixtures/android/`.
|
||||
`verify:android` and `fixtures:android` keep their command names and repository-root
|
||||
`_reference/apk-integration/` fixture default. Synthetic producer fixtures stay in
|
||||
`tests/fixtures/android/`, with matching boundary and composition ownership. Real
|
||||
JADX CLI/MCP parity requires the fixed APK and existing audited JAR; synthetic
|
||||
protocol/cancellation success does not establish that engine or an unverified host.
|
||||
|
||||
## Deep-provider source declarations
|
||||
|
||||
Hopper, Ghidra and IDA declare their implemented analyst operations in their
|
||||
own `*ProviderCapabilities.ts` modules. Hopper's inventory is typed against the
|
||||
existing direct-operation contracts; Ghidra reuses its inventory/function adapter
|
||||
operations; IDA retains its read-only operation family. Frozen operation arrays
|
||||
feed the existing capability builders without importing generated catalog data.
|
||||
|
||||
The catalog generator reads these source declarations and joins operation names
|
||||
to canonical `TOOL_CONTRACTS`. A missing contract identifies the declaring provider
|
||||
and operation. Public schemas and descriptions remain owned by contracts; the
|
||||
declarations describe implementation coverage. Provider adapters must not consume
|
||||
the generated MCP catalog that is derived from them. The resolved module guard
|
||||
checks this edge while admitting canonical source contracts, generated package
|
||||
metadata and the existing application snapshot-cache consumer.
|
||||
|
||||
Exact provider effects and lifecycle differences remain in their adapters:
|
||||
Hopper's GUI and mutation effects, Ghidra's experimental Windows authority and
|
||||
mutation restrictions, and IDA's attached/headless effects and live observations.
|
||||
This source increment introduces no common provider lifecycle or new interface.
|
||||
|
||||
## JavaScript application ownership
|
||||
|
||||
`src/application/javascript/` owns JavaScript/Electron artifact analysis and
|
||||
graph projection, feature/version/export/source comparisons, runtime Evidence
|
||||
and reconciliation, observation ports, and source recovery workflows. Pure
|
||||
analysis and public schemas keep their existing domain/contract ownership;
|
||||
shared artifact inventory, investigation records and execution types remain
|
||||
shared. CLI, MCP and production factories import the owning workflow directly.
|
||||
|
||||
`src/domain/javascript/` owns static parsing and semantic IR, application and
|
||||
semantic graphs, Electron facts, runtime reconciliation, recovery result
|
||||
semantics, and source-to-bundle comparisons. Shared Evidence, digests, ordering,
|
||||
comparison semantics and historical-source graphs stay at their existing owners.
|
||||
Native handoffs link exact-subject Evidence without selecting or executing a
|
||||
provider.
|
||||
|
||||
`src/contracts/javascript/` owns the JavaScript/Electron observation and recovery
|
||||
contracts, JavaScript-specific workflow requests, and their examples. The shared
|
||||
`applicationToolContracts.ts` aggregate stays at the contract root because it also
|
||||
owns reconstruction and Android/Apple projection operations. Canonical tool
|
||||
aggregation, named handler binding and SDK schema presentation remain unchanged.
|
||||
Catalog source paths, compiled verifier/SDK fixtures and recovery CI filters
|
||||
follow the domain/contract owners.
|
||||
|
||||
`scripts/verify/javascript/` groups application-workflow, runtime-reconciliation,
|
||||
large-digest and real-recovery entrypoints. Their existing npm commands retain
|
||||
their invocation semantics. Recovery fixture compilation lives beside its
|
||||
unchanged sources under `scripts/fixtures/javascript-recovery/`. Its default
|
||||
REA entrypoint still resolves to `scripts/rea.mjs`. Managed conformance imports
|
||||
the relocated compiled workflow; owning CI source filters follow the new paths.
|
||||
|
||||
`src/artifacts/javascript/` owns directory/ASAR reader construction and relevant
|
||||
file acquisition, including root/nested byte verification, UTF-8 diagnostics and
|
||||
nested reader cleanup. Its narrow factory returns the existing `ArtifactReader`;
|
||||
the application retains input admission, inventory, graph construction and its
|
||||
original root-reader `finally` close. Pure file facts live in
|
||||
`src/domain/javascript/javascriptArtifactFiles.ts`, and the shared immutable
|
||||
inventory snapshot lives in `src/domain/artifactInventorySnapshot.ts`. Inventory
|
||||
options and integrity policy remain application-owned. Shared stream hashing
|
||||
lives in `src/artifacts/ArtifactHash.ts`, preserving complete byte counts and the
|
||||
bounded classification prefix. Acquisition/hash guards reject imports from
|
||||
application, composition or caller adapters; JavaScript workflows use the factory
|
||||
instead of importing concrete ASAR/directory reader classes.
|
||||
|
||||
ZIP, native-DMG and Mach-O inventory reader selection keep their existing
|
||||
lifecycles. Runtime ports retain their distinct Inspector, passive Electron and
|
||||
active Electron effects; shipped Electron hooks remain at their existing
|
||||
provider boundary.
|
||||
|
||||
## Managed capability ownership
|
||||
|
||||
`src/domain/managed/` owns managed artifact and member semantics, reconstruction
|
||||
import, native-boundary verification and application graph projection.
|
||||
`src/application/managed/` owns their shared Evidence workflows, and
|
||||
`src/contracts/managed/` owns their named contracts and examples. Producer
|
||||
metadata/IL parsing stays in `src/dotnet/`; target resolution, Evidence, ordering
|
||||
and investigation records retain their shared owners. Path-based member
|
||||
comparison preserves its actual target/byte-digest admission and existing parser
|
||||
helper.
|
||||
|
||||
`scripts/verify/managed/` groups the portable conformance entrypoint and its
|
||||
support, manifest, oracle and completion-report helpers. Its PE byte fixture
|
||||
builder and declaration live in `scripts/fixtures/managed/`, shared with the
|
||||
installed-package lane and existing managed conformance tests. `verify:managed`
|
||||
keeps its command name and optional caller-cwd manifest resolution. Compiled
|
||||
catalog and generator imports follow the owners; document-check cache inputs
|
||||
include the relocated runtime verifier and fixture files.
|
||||
|
||||
Portable byte fixtures, optional operator-local manifests, actual ILSpy oracles
|
||||
and real Ghidra NativeAOT checks retain their distinct proof levels and
|
||||
prerequisites. The existing NativeAOT fixture/engine lane keeps its owner. This
|
||||
move preserves implementations, Evidence identities and generated commitments.
|
||||
|
||||
## Binary application ownership
|
||||
|
||||
`src/application/binary/` owns the active session, deep-provider registry and
|
||||
evaluation, operation routing, disjoint provider composition, lazy auxiliary
|
||||
clients, cancellation and client cleanup. Its records facade and snapshot
|
||||
cache/files remain bound to the exact target and analysis profile. Investigation
|
||||
records still have their separate owner in `src/application/investigation/`.
|
||||
|
||||
`AnalysisProvider.ts` stays shared because its identity/execution types also serve
|
||||
nonbinary ports. `BinaryTargetResolver.ts` stays shared because Android, firmware,
|
||||
managed and artifact workflows use its target parsing. Production wiring lives
|
||||
in `src/composition/binary.ts`; application workflows gain no outward composition
|
||||
dependency.
|
||||
|
||||
The three colocated application tests move with their owner. Composition cases
|
||||
remain in `tests/composition/analysis-sessions/`; filesystem and SDK cases retain
|
||||
their boundary lanes. The shared `tests/fixtures/binarySession.ts` factory supports
|
||||
several capability families. Existing recursive application globs discover the
|
||||
moved tests; bridge assets and real-verifier entrypoints remain at their owners.
|
||||
|
||||
## Firmware layer and verification ownership
|
||||
|
||||
Firmware workflows and their existing typed port live in
|
||||
`src/application/firmware/`, pure request/result semantics in
|
||||
`src/domain/firmware/`, and named contracts in `src/contracts/firmware/`.
|
||||
`src/firmware/` keeps producer commands, report parsing, source copies, publication
|
||||
and cleanup; `src/composition/firmware.ts` supplies a fresh typed factory.
|
||||
|
||||
The real lane belongs to `scripts/verify/firmware/analysis.mjs`; the fixture runner
|
||||
and its unchanged Python producer belong together in `scripts/fixtures/firmware/`.
|
||||
The npm command names and selected `REA_FIRMWARE_FIXTURE_ROOT` are unchanged. The
|
||||
runner retains its working-directory-relative default; the verifier retains its
|
||||
repository-root default. Both point to `_reference/firmware-integration/generated`
|
||||
when invoked through the documented npm commands. Optional ext4/Ghidra lanes keep
|
||||
their own prerequisites. Source-fixture generation and synthetic producer tests
|
||||
do not establish real Binwalk/Unblob analysis or another host's execution support.
|
||||
|
||||
## Apple artifact capability ownership
|
||||
|
||||
`src/domain/apple/` owns Apple application anatomy/projection, asset catalog
|
||||
facts, Interface Builder graphs, keyed archive semantics and plist values.
|
||||
`src/application/apple/AppleApplicationService.ts` retains the shared workflow
|
||||
that projects authenticated IPA/macOS inventory Evidence.
|
||||
|
||||
`src/artifacts/apple/` owns the actual producer boundary: directory acquisition,
|
||||
assetutil invocation, plist/NIB decoding, encoded view-parent interpretation and
|
||||
keyed archive inspection. These helpers are used by ArtifactProvider, retain
|
||||
its existing typed analysis boundary, injected assetutil seam and original reader
|
||||
cleanup, and do not require a new port for each helper. Producer code no longer
|
||||
lives in the application layer. Guards reject application/composition/caller
|
||||
imports from this owner while admitting pure facts and shared artifact readers.
|
||||
Adapter-source tests retain their assertions in the existing forked adapter lane.
|
||||
|
||||
`scripts/verify/apple/` groups archive, asset catalog, Interface Builder,
|
||||
Objective-C/Swift dispatch and macOS bundle verification. The macOS bundle fixture
|
||||
builder lives in `scripts/fixtures/apple/`; NIB byte fixtures stay beside their
|
||||
artifact decoder. Existing format-specific conformance sources and goldens keep
|
||||
their locations, and relocated verifiers resolve them against their own file
|
||||
URLs. Public npm entrypoints and CLI/MCP contracts stay stable; generated-document
|
||||
cache inputs include the relocated runtime scripts and fixture builder.
|
||||
|
||||
Portable decoder and injected producer tests establish their format/boundary
|
||||
claims. Actual Swift/Xcode, assetutil, native NIB, bundle signing and DMG lifecycle
|
||||
claims require the real Apple lane. Native binary API/value-flow semantics and
|
||||
shared cross-domain contract aggregates keep their separate owners.
|
||||
|
||||
## Artifact inventory and extraction ownership
|
||||
|
||||
`src/artifacts/inventory/` owns format classification, reader selection, inventory
|
||||
policy, canonical scanning and artifact graph construction. The shared artifact
|
||||
provider and JavaScript reconstruction workflow consume this owner. Snapshot
|
||||
semantics remain in `src/domain/artifactInventorySnapshot.ts`.
|
||||
|
||||
`src/artifacts/extraction/` owns member selection, integrity checks and transactional
|
||||
materialization through the existing readers and safe output tree. Reader cleanup,
|
||||
cancellation, rollback, archive containment and caller-visible results retain their
|
||||
existing behavior. Acquisition guards reject imports from these producer owners
|
||||
into application workflows, production composition or CLI/MCP adapters.
|
||||
|
||||
`src/application/artifacts/ArtifactExtractionDestination.ts` owns the caller's
|
||||
default temporary output-root choice for both CLI and MCP. The adapters pass this
|
||||
destination to the existing provider operation; format handling and member writes
|
||||
remain in the artifact owner.
|
||||
|
||||
Add format-specific acquisition behavior beside the inventory readers and exercise
|
||||
its actual producer representation in filesystem boundary tests. Add caller
|
||||
workflow behavior in the application owner and verify both adapters. The installed
|
||||
package lane checks inventory identity and extraction through CLI and MCP; the
|
||||
real Apple bundle lane separately establishes DMG mount and detach behavior.
|
||||
|
||||
## Binary production composition closure
|
||||
|
||||
`src/composition/binary.ts` is the shared production owner for deep candidate
|
||||
construction, lazy auxiliary composition and the managed-only session factory.
|
||||
It preserves the existing constructor bodies, selection policy and fresh session
|
||||
per invocation. Factory registration acquires no engine or target.
|
||||
|
||||
`DirectAnalysis` and `DirectAnalysisStatus` receive these two existing factories
|
||||
through `DirectAnalysisDependencies`. They retain configuration parsing, selected
|
||||
status environment, cancellation, snapshot replay/binding, Evidence projection
|
||||
and finally-close. The dependency uses the existing BinarySession type, including
|
||||
its replay policy; it adds no universal backend or lifecycle contract.
|
||||
|
||||
`src/composition/directAnalysis.ts` binds the production factories for CLI and
|
||||
one-shot managed MCP callers. Binding creates no session and stores no retained
|
||||
analysis state. MCP startup constructs its connection session through the same
|
||||
binary factory. Source and compiled fixtures use these entrypoints. The former
|
||||
application runtime path is removed, and the source guard no longer admits its
|
||||
outward provider/composition imports.
|
||||
|
||||
## Native analyst capability ownership
|
||||
|
||||
`src/domain/native/` owns portable native inspection, instruction/data types,
|
||||
load-image facts, API boundaries, annotations, metadata recovery, UI observation
|
||||
semantics and value/investigation graphs. `src/application/native/` owns shared
|
||||
API projection, call routes, dispatch inspection and UI/value tracing. Its tests
|
||||
keep the existing service lane. `src/contracts/native/` owns the named native
|
||||
adapter contracts; shared official/enhanced aggregates remain at their owners.
|
||||
|
||||
Producer interpretation, external commands and host UI lifecycle remain in
|
||||
`src/native/`, Hopper, Ghidra and IDA. The shared function dossier still lives in
|
||||
`domain/hopperValues.ts`: its historical name does not make its meaning exclusive
|
||||
to Hopper. This move retains existing typed ports, exact result schemas,
|
||||
observed/derived/unknown distinctions, profiles and Evidence links.
|
||||
|
||||
Source and compiled verifier imports follow the new owners. Host UI and real
|
||||
native-value verifier locations are retained until their own proof lanes migrate;
|
||||
interactive macOS UI success and real dependency tracing require their respective
|
||||
host/engine workflows. Portable source fixtures and injected call-route tests
|
||||
establish their narrower boundaries, without expanding provider/platform support.
|
||||
|
||||
## Process capture and analyst workflow ownership
|
||||
|
||||
`src/domain/process/` owns scenario/capture validation, portable process trees,
|
||||
trace specifications, observations, comparisons and the canonical process Evidence
|
||||
identity. The standalone contract example lives in `src/contracts/process/`;
|
||||
shared session contracts retain their broader investigation owner.
|
||||
|
||||
`src/process/capture/` owns the actual PTY boundary, terminal rendering, sampling,
|
||||
selected child environment, filesystem snapshots/effects, runtime path admission,
|
||||
event journal, settlement and resource cleanup. It reuses `src/process/` ownership
|
||||
primitives and the Windows host substrate. Its identity import follows the
|
||||
existing pure declaration directly. A source guard rejects outward application,
|
||||
composition and caller imports; no new provider or lifecycle interface is added.
|
||||
|
||||
`src/application/process/` retains Evidence projection and file-backed CLI
|
||||
capture/comparison workflows. Capture helper tests use the forked adapter lane;
|
||||
Evidence/host integration tests use the serial process-boundary lane. Their
|
||||
fixtures and assertions remain unchanged. Installed capability-probe URLs follow
|
||||
the new compiled adapter path, including the optional-dependency failure probe.
|
||||
|
||||
Windows PTY capture retains its existing unavailable outcome because descendant
|
||||
cleanup is not yet verified. Native Job Objects and a PTY binary do not establish
|
||||
that capture workflow. Real POSIX capture/cleanup and installed terminal/PTY module
|
||||
resolution require their actual host and package lanes; portable comparison
|
||||
fixtures establish their separate evidence semantics.
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
# CLI and Evidence
|
||||
|
||||
Use the CLI for a direct inspection or a script. It uses the same application
|
||||
workflows and evidence contracts as REA's MCP server. Each CLI invocation is a
|
||||
separate process; save results when you want to use them in a later command.
|
||||
|
||||
## Run a command
|
||||
|
||||
After [installing REA](installation.md), use `rea`. You can also run a command
|
||||
without a global installation:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
Use an extracted application directory or an ASAR as the target. Static
|
||||
JavaScript analysis returns the application graph and Evidence directly.
|
||||
Generic `rea analyze PATH` selects this workflow for directories and `.asar`
|
||||
files when neither `--provider` nor `--snapshot` is supplied. See
|
||||
[JavaScript artifact reconstruction](javascript-artifact-reconstruction.md)
|
||||
for results, integrity checks and coverage.
|
||||
|
||||
## Native analysis
|
||||
|
||||
Configure [Hopper or Ghidra](installation.md#hopper), or the
|
||||
[IDA adapter](ida-provider.md), before analyzing a native target. Substitute
|
||||
your target, search text and function name or address in these examples:
|
||||
|
||||
```bash
|
||||
rea analyze /absolute/path/to/program --provider ghidra --json
|
||||
rea search /absolute/path/to/program "search" --provider ghidra --json
|
||||
rea function /absolute/path/to/program main --provider ghidra --json
|
||||
rea decompile /absolute/path/to/program 0x1000 --provider ghidra --json
|
||||
rea xrefs /absolute/path/to/program 0x1000 --provider ghidra --json
|
||||
rea trace /absolute/path/to/program "search" --provider ghidra --json
|
||||
```
|
||||
|
||||
`analyze` and `inspect` share the native overview workflow. `function` returns
|
||||
a function dossier; `decompile` returns pseudocode. For assembly instructions
|
||||
alone, use `rea instructions`. A macOS `.app` bundle can be supplied directly.
|
||||
|
||||
Run `rea --help` or a command's `--help` for arguments and output options.
|
||||
The [generated catalog](product-catalog.json) lists all CLI commands and MCP
|
||||
tools. Provider-specific target support is described in the
|
||||
[native guide](native-investigation.md), [DOS guide](ghidra-dos.md), and
|
||||
[Windows Ghidra guide](windows-ghidra-p0.md).
|
||||
|
||||
## Choose a provider
|
||||
|
||||
List the available providers and their supported operations:
|
||||
|
||||
```bash
|
||||
rea providers --json
|
||||
rea capabilities --json
|
||||
```
|
||||
|
||||
These commands describe binary-session providers and auxiliary capabilities.
|
||||
For the complete MCP surface, use the connected server's tool list and the
|
||||
session's advertised availability; individual guides describe prerequisites.
|
||||
|
||||
With automatic selection, REA uses the single available provider that supports
|
||||
the target. When several providers support it, select one explicitly:
|
||||
|
||||
```bash
|
||||
rea analyze /absolute/path/to/program --provider hopper
|
||||
```
|
||||
|
||||
Set `REA_ANALYSIS_PROVIDER` for a standing preference. An explicit `--provider`
|
||||
overrides it. In MCP, pass `provider_id` to `open_binary`:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/absolute/path/to/program",
|
||||
"provider_id": "hopper"
|
||||
}
|
||||
```
|
||||
|
||||
The selected provider remains bound to the session until an explicit switch
|
||||
or close. Provider failures are returned with their original reason. For an
|
||||
`ambiguous` selection error, choose from `details.candidate_ids`; for
|
||||
`provider_unavailable`, run `rea doctor --provider ID --json` to diagnose the
|
||||
selected engine. See [task readiness](installation.md#check-readiness-for-your-task)
|
||||
and [provider selection](adr/0001-provider-selection-and-analysis-profiles.md).
|
||||
|
||||
The session reports work still in progress through `analysis_activity`.
|
||||
A client timeout can end its wait while the provider continues analyzing.
|
||||
`cleanup_incomplete` identifies resources whose shutdown or removal could not
|
||||
be verified. See [MCP contracts](mcp-contracts.md) for session lifecycle and
|
||||
[Ghidra first-query deadlines](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
|
||||
for import, client timeouts and recovery.
|
||||
|
||||
## Save and reuse analysis snapshots
|
||||
|
||||
A snapshot retains successful analysis results for later queries. REA reuses
|
||||
an exact result when the target bytes, operation, parameters, provider and
|
||||
settings match. Mutations and cursor-dependent calls are excluded from the
|
||||
cache. Snapshot files are local and use owner-only permissions.
|
||||
|
||||
```bash
|
||||
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
|
||||
# Repeat the same query to reuse its saved result.
|
||||
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
|
||||
```
|
||||
|
||||
An exact CLI cache hit is read before starting a provider process.
|
||||
In MCP, `open_binary` accepts `snapshot_path` to import a snapshot atomically
|
||||
for its matching target; an MCP provider may still start before returning a
|
||||
cached result. `close_binary` accepts `snapshot_path` and optional
|
||||
`overwrite: true` to save before releasing provider resources. A failed save
|
||||
leaves the session open so the caller can resolve the output failure.
|
||||
|
||||
The MCP save receipt reports `primitive_entries`, `workflow_entries`, and
|
||||
`evidence_records` separately. Zero primitive bindings can still accompany retained workflow
|
||||
results and Evidence. These are cached observations, not a saved provider
|
||||
database: only eligible exact queries can reuse a result, and new or live
|
||||
queries can still require provider startup. The CLI uses the same snapshot
|
||||
format and preserves these records when loading and updating it.
|
||||
|
||||
## Import, export and compare Evidence
|
||||
|
||||
Evidence records retain artifact identity, source locations, observations,
|
||||
inferences and unresolved findings. Validate an existing bundle, export its
|
||||
canonical form, or compare two supplied bundles:
|
||||
|
||||
```bash
|
||||
rea evidence-import /absolute/path/to/evidence/bundle.json
|
||||
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
||||
rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
|
||||
```
|
||||
|
||||
Exports preserve an existing destination unless `--overwrite` is explicit.
|
||||
For application traces and version comparisons, see
|
||||
[JavaScript application workflows](javascript-application-workflows.md).
|
||||
MCP [retained Evidence references](mcp-contracts.md#retained-application-evidence-inputs)
|
||||
belong to one connection. Separate CLI calls consume full saved records.
|
||||
|
||||
## Import historical source
|
||||
|
||||
Import an older source tree to compare with the current artifact:
|
||||
|
||||
```bash
|
||||
rea import-reference-source /absolute/path/to/source
|
||||
```
|
||||
|
||||
The import records hashes and metadata for the supplied files, separately
|
||||
from observations of the current app. File names do not automatically exclude
|
||||
files. Set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON array of ignore
|
||||
patterns when you want to exclude selected paths.
|
||||
|
||||
Historical-source import requires safe no-follow file opens on Linux or
|
||||
macOS. Native Windows returns `unsupported_host`; use Linux REA inside WSL
|
||||
or another supported host. See
|
||||
[source-to-bundle comparison](javascript-application-workflows.md#historical-source-to-bundle-comparison)
|
||||
for mapping identities, inferences and unknowns.
|
||||
|
||||
## Capture runtime behavior
|
||||
|
||||
Choose the guide for the target and intended interaction:
|
||||
|
||||
- [Browser observation](browser-observation.md): inspect a selected page through CDP.
|
||||
- [Browser scenarios](browser-scenario-contract.md): run declared interactions and capture their results.
|
||||
- [Electron observation](electron-observation.md): inspect an Electron renderer or capture an application scenario.
|
||||
- [Node/Electron Inspector](javascript-runtime-observation.md): record script locations and execution contexts.
|
||||
- [Process capture](process-capture.md): run an executable and compare terminal, exit and filesystem observations.
|
||||
|
||||
Runtime requests name the target and actions. Launched targets run with your
|
||||
user permissions; consult the chosen guide for host requirements and effects.
|
||||
|
||||
## Output and exit status
|
||||
|
||||
The default terminal format is TOON. Use `--json` when saving results for a
|
||||
JSON consumer. Output selection and formatting do not change operation status.
|
||||
|
||||
| Status | Meaning |
|
||||
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `0` | The operation completed. Its result may include partial evidence, warnings or unresolved questions. |
|
||||
| `1` | The operation could not complete. Structured output identifies invalid input, permissions, cancellation, timeouts or another failure when available. |
|
||||
| `128 + N` | Signal `N` ended the process, where the shell or runtime preserves the conventional signal-derived status. |
|
||||
|
||||
`setup --dry-run` returns `planned` and exits `0`; a cancelled setup also exits
|
||||
`0`. Setup returns `1` for `needs_confirmation` or `needs_human`.
|
||||
`doctor` returns `1` when required checks in its selected readiness scope fail;
|
||||
unavailable optional providers remain informational for unrelated tasks.
|
||||
|
||||
Enable `pipefail` in a supporting shell so a downstream formatter preserves
|
||||
REA's failure status:
|
||||
|
||||
```bash
|
||||
set -o pipefail
|
||||
rea inspect-artifact ./app.asar --json | jq . > inspection.json
|
||||
```
|
||||
@@ -86,16 +86,33 @@ rea capture-electron-scenario scenario.json --json
|
||||
|
||||
The result records action status and targets, correlated app/window/WebContents,
|
||||
preload, session, navigation, shell, permission, popup, download, protocol,
|
||||
native-addon, process, and IPC timeline events. IPC channels are capped at
|
||||
1,024 characters and argument-shape metadata at 32 entries; values are never
|
||||
retained. Renderer crash/restart and deep-link actions are synthetic
|
||||
native-addon, process, and IPC timeline events. IPC channels and argument-shape
|
||||
metadata retain their observed lengths; payload values are never retained.
|
||||
Renderer crash/restart and deep-link actions are synthetic
|
||||
scenario controls; their attempted and observed outcomes remain in the
|
||||
timeline. The active hook blocks and records external shell/navigation,
|
||||
permission, download, popup, updater, and OS-integration effects. The timeline
|
||||
is explicitly partial when attachment starts after application activity. The
|
||||
owned experiment has an internal 60-second deadline and a 5-second per-action
|
||||
timeout so hung work reaches process cleanup; these are lifecycle timeouts, not
|
||||
caller-selected capture limits.
|
||||
startup has a 60-second deadline. Actions wait until completion or request
|
||||
cancellation; a click whose target never appears remains pending until cancelled.
|
||||
CLI SIGINT/SIGTERM requests cancellation and awaits owned-process cleanup.
|
||||
Window IDs, URLs, and cached titles come from one main-process Electron snapshot,
|
||||
so a crashed renderer does not require a successful renderer-side title query.
|
||||
Window instrumentation uses Electron's window-created event, including runtimes
|
||||
with a read-only `BrowserWindow` export. Preload configuration comes from the
|
||||
window's runtime metadata, including Electron's optional internal preload
|
||||
descriptor when web preferences omit it. This descriptor is verified on
|
||||
Electron 44.7.0; runtimes that expose neither representation retain an unavailable
|
||||
path. A configured path or working IPC handler does not prove preload execution.
|
||||
If cleanup cannot be verified, the error retains collected observations in
|
||||
`details.partial_observation` and marks their application cleanup `unverified`.
|
||||
Cleanup uncertainty does not authorize signaling unrelated processes.
|
||||
|
||||
The real verifier exercises compiled CLI and stdio MCP discovery, page/script
|
||||
inspection, static/runtime reconciliation, active observation, and CLI SIGTERM
|
||||
cleanup before checking the IPC, renderer crash/reload, utility/child-process,
|
||||
deep-link, and blocked-effect fixture. A cleanup error keeps the full lane failed
|
||||
even when the retained capture contains useful observations.
|
||||
|
||||
Active Electron Evidence can also be supplied to
|
||||
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md). That
|
||||
|
||||
+192
-32
@@ -10,10 +10,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -29,7 +31,79 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "access_denied" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "artifact_changed" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -44,10 +118,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -63,7 +139,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -78,10 +154,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -97,7 +175,43 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "unsupported_target" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -112,10 +226,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -131,7 +247,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -146,10 +262,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -165,7 +283,43 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "resource_constraint" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -180,10 +334,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -199,7 +355,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -214,10 +370,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -270,10 +428,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -289,7 +449,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -304,10 +464,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -323,7 +485,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -338,10 +500,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -357,7 +521,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -372,10 +536,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -391,7 +557,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -406,10 +572,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -425,7 +593,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -440,10 +608,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -459,7 +629,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -474,10 +644,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -493,7 +665,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -508,10 +680,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -527,7 +701,7 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
@@ -542,10 +716,12 @@
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"unsupported_provider",
|
||||
"unsupported_target",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"resource_constraint",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
@@ -561,27 +737,11 @@
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"$defs": {
|
||||
"__schema0": {
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "number" },
|
||||
{ "type": "boolean" },
|
||||
{ "type": "null" },
|
||||
{ "type": "array", "items": { "$ref": "#/$defs/__schema0" } },
|
||||
{
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Offline EVM bytecode
|
||||
|
||||
`inspect_evm_interface` / `inspect-evm-interface` infers interface candidates
|
||||
from one explicitly selected local bytecode carrier. The capability is general:
|
||||
contract investigation, compatibility, audit and CTF showcases use the same
|
||||
portable primitive. It does not require an active native binary target.
|
||||
|
||||
```sh
|
||||
rea inspect-evm-interface ./runtime.hex hex --json
|
||||
rea inspect-evm-interface ./runtime.bin raw --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_evm_interface",
|
||||
"arguments": { "path": "/artifacts/runtime.hex", "encoding": "hex" }
|
||||
}
|
||||
```
|
||||
|
||||
Initial real verification covers flat legacy bytecode on Linux x64 and unchanged
|
||||
bundled EVMole 0.9.3. EOF-style EF00 containers return `unsupported_target`,
|
||||
with the selected path and format reason. Provider/configuration failures retain
|
||||
their distinct unavailable-provider or capability classification.
|
||||
The host must already provide util-linux `prlimit`, normally `/usr/bin/prlimit`;
|
||||
`REA_EVM_PRLIMIT_COMMAND` can select another absolute executable. REA installs
|
||||
no system tools and changes no user configuration. [Upstream provenance and
|
||||
license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md) are packaged.
|
||||
|
||||
## Evidence semantics
|
||||
|
||||
- `encoding` is required: raw bytes or UTF-8 hex, with optional `0x` prefix and
|
||||
outer ASCII whitespace. Odd hex, interior separators, BOM, non-ASCII whitespace
|
||||
and invalid UTF-8 are rejected. Empty bytecode is valid and has unknown coverage.
|
||||
- Carrier SHA-256 identifies the exact original file, including prefix/casing
|
||||
and whitespace. Decoded-byte SHA-256 identifies actual selected EVM bytes.
|
||||
Neither digest is the Ethereum Keccak code hash.
|
||||
- Full bytecode, upstream function offsets and original producer representation
|
||||
are returned inline. Offsets are byte positions in decoded bytecode.
|
||||
- Upstream also returns terminal metadata during selector recovery. Its reported
|
||||
values remain in `raw_result`; integer precision and non-text-key coverage are
|
||||
unknown in the JS binding. Full bytecode retains the original encoded metadata.
|
||||
- Selectors, argument strings and state mutability are **inferences**. Function
|
||||
names/signatures are not looked up. Optimized code may produce different
|
||||
mutability candidates; recovered types are not a verified source ABI.
|
||||
- Runtime/init-code identity, deployed authenticity, hardfork and discovery
|
||||
completeness remain unknown. Empty candidates do not prove no callable interface.
|
||||
- Analysis is offline: no target runtime execution, implicit chain/RPC/explorer
|
||||
lookup, wallet action, transaction broadcast or network request.
|
||||
|
||||
## Resource and lifecycle limits
|
||||
|
||||
Inputs are limited to 4 MiB, complete replies to 16 MiB and combined diagnostic
|
||||
output to 1 MiB. An owned worker has a 256 MiB JavaScript heap, 3 GiB virtual
|
||||
address-space limit, 30 CPU seconds and a 30-second command deadline. Virtual
|
||||
address space differs from resident memory. The worker retains inherited tighter
|
||||
soft/hard limits, reports its configured soft limits inline and leaves hard limits
|
||||
unchanged. WASM trap reservations are disabled
|
||||
so the engine can run within that virtual limit; actual memory is not equated to
|
||||
its 3 GiB allowance. Limits fail without partial success. Stable snapshots,
|
||||
private files and owned processes are cleaned independently of caller cancellation.
|
||||
Invalid-input, unsupported, cancellation, timeout and malformed-reply failures
|
||||
retain bounded stdout/stderr under `details.captured_output`, including whether
|
||||
diagnostic output was truncated. Cleanup failures preserve the
|
||||
original error projection and any captured output.
|
||||
Successful results expose `diagnostics.truncated` from the supervisor as well.
|
||||
Excess diagnostic output fails the complete-output contract even when the worker
|
||||
has written a valid reply.
|
||||
SIGXCPU retains a CPU resource diagnostic and CPU-specific guidance. Observed
|
||||
file-size write failures (EFBIG) and SIGXFSZ likewise retain a file-size resource
|
||||
diagnostic. Exact signal/write causes remain unknown. Configured
|
||||
soft limits are reported separately from unknown effective limits; the received
|
||||
signal alone does not establish its exact cause.
|
||||
Reserved file-size exits require a matching private marker written by the
|
||||
worker's EFBIG branch. Bare launcher exits or unwritable markers retain process
|
||||
diagnostics with the resource cause unverified.
|
||||
|
||||
Source/metadata, proxy resolution, storage/CFG and fork-explicit execution/traces
|
||||
remain separate increments under #972, with materially different result or
|
||||
execution authority. This primitive makes no deployment or runtime behavior claim.
|
||||
|
||||
The real verification lane traces representative raw/hex CLI workflows and checks
|
||||
that only declared Node/prlimit launchers and process ownership inspection run.
|
||||
Socket traces permit local stdio socket pairs and reject attempted network requests.
|
||||
These observations verify those fixtures; they are not a general network sandbox.
|
||||
@@ -0,0 +1,178 @@
|
||||
# Firmware analysis
|
||||
|
||||
REA accepts **Binwalk 3.1.x** for region inspection and **Unblob 26.6.x** for
|
||||
explicit extraction. The report parsers are verified with Binwalk 3.1.0 and
|
||||
Unblob 26.6.4. Another build on those lines is accepted, the observed version
|
||||
is reported, and a limitation records that the parser was not verified against
|
||||
that build. This first firmware milestone is verified on Linux x64.
|
||||
No device, root privilege, mount, emulation or execution of extracted programs
|
||||
is needed. Arbitrary firmware bytes do not pass native executable admission;
|
||||
select a returned ELF separately for supported Ghidra analysis.
|
||||
|
||||
## Caller-supplied tools
|
||||
|
||||
Provide existing tools using absolute executable paths, scoped to your shell:
|
||||
|
||||
```sh
|
||||
export REA_BINWALK_COMMAND=/absolute/path/to/binwalk
|
||||
export REA_UNBLOB_COMMAND=/absolute/path/to/unblob
|
||||
```
|
||||
|
||||
REA does not install these tools, their dependencies or system packages. It
|
||||
checks the tool's `--version` banner when executing a request. Binwalk must
|
||||
print `binwalk <version>` and Unblob must print the version alone. Inspection and
|
||||
extraction have independent prerequisites; one does not require the other.
|
||||
Both require util-linux `prlimit`, normally `/usr/bin/prlimit`; override its
|
||||
absolute path with `REA_FIRMWARE_PRLIMIT_COMMAND` if needed.
|
||||
|
||||
Unblob needs the external extractor for the selected format on its inherited
|
||||
`PATH`: for example, `debugfs` for ext2/ext3/ext4. Its default Landlock sandbox
|
||||
and skip rules stay enabled. Sandbox startup failures remain actionable errors;
|
||||
REA does not retry with `--no-sandbox`. Missing extractors reported in a valid
|
||||
analysis report retain partial results and dependency names inline.
|
||||
|
||||
Upstream sources are preserved unmodified as optional pinned submodules:
|
||||
|
||||
```sh
|
||||
git submodule update --init third_party/binwalk third_party/unblob
|
||||
```
|
||||
|
||||
Normal REA builds and npm packages do not include or build these sources or
|
||||
engine binaries. See [upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md).
|
||||
|
||||
## CLI and MCP
|
||||
|
||||
```sh
|
||||
rea inspect-firmware-regions /path/to/firmware.bin
|
||||
rea extract-firmware /path/to/firmware.bin /path/to/new-output
|
||||
rea extract-firmware /path/to/firmware.bin /path/to/selected-output \
|
||||
--offset 64 --length 4096 --max-depth 3
|
||||
```
|
||||
|
||||
The corresponding MCP tools accept the same intent:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_firmware_regions",
|
||||
"arguments": { "path": "/path/to/firmware.bin" }
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "extract_firmware",
|
||||
"arguments": {
|
||||
"path": "/path/to/firmware.bin",
|
||||
"output_directory": "/path/to/new-output",
|
||||
"range": { "offset": 64, "length": 4096 },
|
||||
"max_depth": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Both return Evidence with the original path and SHA-256, raw producer reports,
|
||||
actual executable path/digest, version and limitations. Binwalk signature UUIDs
|
||||
and Unblob blob IDs are producer-local identities; compare content hashes and
|
||||
source ranges across runs. A matching version does not attest an installed
|
||||
build's source revision, so that field remains unknown.
|
||||
|
||||
### Interpret the results
|
||||
|
||||
- Region offsets refer to bytes in the original firmware. `reported_size`
|
||||
preserves Binwalk's output; `size_basis=provider_reported_validation_unknown`
|
||||
reflects that Binwalk can replace unknown lengths with the next signature or
|
||||
EOF before serialization. Neither a signature nor its confidence byte proves
|
||||
a valid partition, encryption or runtime behavior.
|
||||
- Extraction returns regular file paths, independently verified SHA-256 values,
|
||||
producer hashes when available, chunks, diagnostics and task derivations.
|
||||
Unblob's task report is unordered; REA resolves relationships by paths and
|
||||
blob IDs. Logical references use `$input`, `$output`, or `$output/<relative-path>`
|
||||
so a real file named `$input` stays distinct from the original input.
|
||||
Chunk offsets belong to the task's input file. Only chunks in the
|
||||
selected input have a known `root_file_range`, adjusted by the selected offset.
|
||||
- Decompressed children have unknown original byte ranges and runtime addresses.
|
||||
Task lineage does not establish a byte-for-byte mapping through decompression.
|
||||
- `partial` marks unknown/encrypted chunks, reported extraction problems,
|
||||
unpublished links/special files or reached depth limits. `complete` means the
|
||||
engine finished its configured extraction without those reported conditions;
|
||||
default skip rules and format coverage still apply. It does not establish
|
||||
complete firmware reconstruction.
|
||||
- The destination must be absent, absolute, with an existing parent. Publication
|
||||
uses exclusive, symlink-resistant writes and durable digest readback. Existing
|
||||
destinations are never overwritten. This is not an atomic directory rename:
|
||||
a new tree can be visible while files are written. Failed uncommitted output
|
||||
is rolled back. Symlinks and special files are recorded without publication;
|
||||
empty directories, ownership and executable permission bits are not reproduced.
|
||||
|
||||
Select a verified native child for the existing tools:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "open_binary",
|
||||
"arguments": {
|
||||
"path": "/path/from/extraction/usr/bin/program",
|
||||
"provider_id": "ghidra"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then use `analyze_function`, `inspect_native_api` or the other native primitives.
|
||||
Native architecture and executable validation still apply. This milestone does
|
||||
not add MIPS/PPC/RISC-V decompilation, raw flash load profiles, bootloader/device
|
||||
tree semantics, vulnerability scanning, emulation or hardware runtime capture.
|
||||
|
||||
## Resource and lifecycle policy
|
||||
|
||||
- Nonempty regular firmware inputs: at most **128 MiB**, copied and hashed into
|
||||
a private workspace before analysis. No in-place source modification.
|
||||
- One queued heavy operation per provider instance; Binwalk one thread and
|
||||
Unblob one worker. No automatic parallel extraction engines.
|
||||
- **120-second** deadline; inherited **1 GiB per-process address-space** limit,
|
||||
120 CPU seconds and zero core-dump allowance through `prlimit`.
|
||||
- JSON reports: at most **8 MiB** and valid UTF-8; malformed encoding and
|
||||
oversized reports fail without repairing or truncating producer data.
|
||||
Diagnostic output is bounded with exact captured byte counts.
|
||||
- Default staging budget: **256 MiB** and **10,000 entries**, configurable using
|
||||
`max_output_bytes` / `max_output_files` (CLI `--max-output-bytes` /
|
||||
`--max-output-files`). Depth defaults to 3, allowed range 1–10.
|
||||
|
||||
Staging budgets are polled and rechecked after exit; short-lived overshoot is
|
||||
possible. The inherited file-size limit bounds each file, not total storage;
|
||||
address-space limits are per process, not an aggregate memory quota. Worker
|
||||
count is not CPU affinity for every external extractor. These limits do not
|
||||
claim cgroup/container containment. Cancellation cleans the owned process group
|
||||
before removing its workspace; uncertain cleanup retains resources and blocks
|
||||
further operations in that provider instance.
|
||||
|
||||
## Verification
|
||||
|
||||
Generate small public, source-derived fixtures into ignored `_reference/`:
|
||||
|
||||
```sh
|
||||
npm run fixtures:firmware
|
||||
npm run verify:firmware
|
||||
```
|
||||
|
||||
Fixture generation requires existing Python 3 and a host C compiler (`cc`);
|
||||
override with `REA_FIRMWARE_PYTHON` / `REA_FIRMWARE_CC`. The independent oracle
|
||||
records gzip offset/length and extracted file hashes. Real verification checks
|
||||
CLI/MCP, full-file and selected-range extraction, unknown chunks, depth limits,
|
||||
content parity and unchanged source bytes. No binary fixture is committed.
|
||||
|
||||
Additional lanes are explicit, so the base lane stays small:
|
||||
|
||||
```sh
|
||||
# Existing mke2fs and debugfs required; put debugfs on the scoped PATH.
|
||||
REA_FIRMWARE_VERIFY_EXT4=1 npm run fixtures:firmware
|
||||
REA_FIRMWARE_VERIFY_EXT4=1 npm run verify:firmware
|
||||
|
||||
# Existing Ghidra 12.1.x and its declared JDK required.
|
||||
REA_FIRMWARE_VERIFY_GHIDRA=1 npm run verify:firmware
|
||||
```
|
||||
|
||||
`REA_FIRMWARE_MKE2FS_COMMAND` selects the optional ext4 fixture producer.
|
||||
`REA_FIRMWARE_FIXTURE_ROOT` selects generated data storage.
|
||||
`REA_FIRMWARE_TEST_ENTRYPOINT` can select an isolated installed npm package's
|
||||
`scripts/rea.mjs` to verify the packaged CLI/MCP workflow. Real coverage here is
|
||||
generated gzip/USTAR/ext4 with a Linux x64 ELF child; SquashFS, UBIFS, proprietary
|
||||
firmware, other hosts and architectures remain unverified.
|
||||
+94
-7
@@ -1,10 +1,10 @@
|
||||
# DOS MZ analysis with Ghidra
|
||||
# DOS MZ and COM analysis with Ghidra
|
||||
|
||||
REA can analyze DOS MZ executables through the bring-your-own Ghidra adapter.
|
||||
The Linux x64 verification lane exercises actual 16-bit disassembly and
|
||||
decompilation through the CLI and stdio MCP. It requires the same Ghidra
|
||||
12.1.4 and 64-bit JDK 21 installation as other Ghidra sessions; no DOS emulator
|
||||
or cross-compiler is required.
|
||||
REA can analyze DOS MZ executables and explicitly selected COM images through the bring-your-own Ghidra adapter.
|
||||
The Linux x64 and macOS arm64 verification lanes exercise actual 16-bit disassembly and
|
||||
decompilation through the CLI and stdio MCP. Provider admission accepts Ghidra
|
||||
12.1.x and that installation's declared JDK range. The verification lane uses
|
||||
Ghidra 12.1.4 and JDK 21; no DOS emulator or cross-compiler is required.
|
||||
|
||||
## Open and inspect
|
||||
|
||||
@@ -13,6 +13,9 @@ rea inspect /absolute/path/to/legacy.exe --provider ghidra --json
|
||||
rea search /absolute/path/to/legacy.exe entry --kind procedures --provider ghidra --json
|
||||
rea instructions /absolute/path/to/legacy.exe entry --provider ghidra --json
|
||||
rea function /absolute/path/to/legacy.exe entry --provider ghidra --json
|
||||
rea inspect-native-load-image /absolute/path/to/legacy.exe --provider ghidra --json
|
||||
rea read-bytes /absolute/path/to/legacy.exe 0x10000 --length 16 --provider ghidra --json
|
||||
rea address-to-file-offset /absolute/path/to/legacy.exe 0x10000 --provider ghidra --json
|
||||
```
|
||||
|
||||
Use an observed procedure name or address from discovery when the target does
|
||||
@@ -38,6 +41,43 @@ The profile also commits `function_body_evidence: complete-inclusive-ranges-v1`,
|
||||
so snapshots from older length-only Ghidra results do not satisfy the current
|
||||
profile.
|
||||
|
||||
## Headerless COM images
|
||||
|
||||
COM has no identifying header. Select its interpretation explicitly; neither the
|
||||
`.com` suffix nor arbitrary unrecognized bytes are enough to admit it.
|
||||
|
||||
```bash
|
||||
rea inspect /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
|
||||
rea function /absolute/path/to/legacy.com 0x10100 --target-format dos-com --provider ghidra --json
|
||||
rea inspect-native-load-image /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "open_binary",
|
||||
"arguments": {
|
||||
"path": "/absolute/path/to/legacy.com",
|
||||
"format": "dos-com",
|
||||
"provider_id": "ghidra"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The caller's interpretation applies to the complete file, even if its filename
|
||||
suggests another format. Files must contain 1..65280 bytes: the 64 KiB COM segment
|
||||
minus the 256-byte PSP prefix. Ghidra uses `BinaryLoader` with real-mode language
|
||||
and maps file offset zero at `1000:0100` (linear `0x10100`). A packaged pre-analysis
|
||||
script seeds the entry function and CS/DS/ES/SS context `0x1000`. These settings
|
||||
and the preparation policy are committed in the analysis profile; a failed
|
||||
preparation prevents the resident bridge from serving results.
|
||||
|
||||
Load-image verification checks all original and modified file bytes, complete
|
||||
source mapping/memory digests, absence of relocations, entry and measured register
|
||||
context. The context is imposed for analysis, not observed from execution. PSP
|
||||
memory, initial stack contents/SP, DOS interrupts and PC-98 devices are not
|
||||
modeled. COM has no MZ header, relocation table or appended-overlay model; all
|
||||
selected bytes belong to its analysis module. The original file stays unchanged.
|
||||
|
||||
## Addresses and function extent
|
||||
|
||||
Returned default-space addresses are linear byte coordinates. A Ghidra
|
||||
@@ -47,6 +87,10 @@ address for subsequent lookups and leaves instruction decoding context with
|
||||
Ghidra. These coordinates are analysis load addresses, not file offsets or
|
||||
observations of a running DOS system.
|
||||
|
||||
Offsets that Ghidra would truncate to a different address are rejected before
|
||||
reading or annotating a function. This also applies to explicit or encoded
|
||||
address-space names; leading zeros preserve the numeric offset.
|
||||
|
||||
`procedure_info` and function dossiers include `body` evidence:
|
||||
|
||||
```json
|
||||
@@ -71,6 +115,30 @@ Ghidra's database observation, not a proof of original source ownership or
|
||||
exhaustive code discovery. Providers that do not report complete ranges return
|
||||
`available: false` and an explicit reason.
|
||||
|
||||
## Loaded-image evidence
|
||||
|
||||
`inspect_native_load_image` independently checks the immutable session snapshot
|
||||
against the loaded Ghidra Program. It measures complete original and modified
|
||||
FileBytes digests, every source mapping and its memory digest, relocation records
|
||||
(including multiplicity), and external entry points. It verifies MZ header and
|
||||
module partitions, relocation fixups and linear coordinates rather than relying
|
||||
on the imported executable SHA alone.
|
||||
|
||||
The result is `verified`, `mismatch`, or `unsupported`. Every check retains its
|
||||
expected and observed values. A memory-digest mismatch identifies the source
|
||||
range's file offset and analysis address; it does not locate the first differing
|
||||
byte inside that range. Mapping ends are **inclusive**, unlike the exclusive ends
|
||||
from `list_segments`. Appended overlays remain in source-file identity and are
|
||||
excluded from initialized module coverage. Uninitialized allocations are reported;
|
||||
their contents and DOS runtime semantics are not verified. Formats other than MZ and explicit COM return
|
||||
`unsupported` with measured observations, not a successful MZ verification.
|
||||
|
||||
`read_bytes` returns initialized provider memory, including loader fixups; an
|
||||
unmapped or uninitialized byte ends the read with an explicit returned length and
|
||||
`complete: false`. `address_to_file_offset` uses the provider's source mapping;
|
||||
unmapped, uninitialized and ambiguous mappings fail explicitly. A relocated word
|
||||
can differ from the original file word even when its source offset is known.
|
||||
|
||||
## Coverage boundaries
|
||||
|
||||
Static decompilation does not execute the target or emulate BIOS, DOS
|
||||
@@ -86,7 +154,7 @@ are separate from the initialized MZ module. Raw COM files and DOS extenders
|
||||
without an admitted MZ real-mode entry are outside this import boundary.
|
||||
|
||||
DOS MZ is available through Ghidra, with no Hopper support claim. It is outside
|
||||
the Windows native PE P0 boundary. macOS DOS verification remains unverified.
|
||||
the Windows native PE P0 boundary. macOS x64 DOS verification remains unverified.
|
||||
|
||||
## Real verification
|
||||
|
||||
@@ -103,3 +171,22 @@ executable fixtures, an emulator, or an existing analysis database.
|
||||
Provider p-code representations can contain process-specific address-space selector
|
||||
tokens. The lane preserves those reported values and compares stable dossier
|
||||
observations rather than asserting cross-process identity of raw p-code.
|
||||
|
||||
## Function annotations
|
||||
|
||||
`annotate_native_function` changes the name and/or comments at one exact local function entry, verifies readback, and returns a refreshed dossier. This is useful when recording recovered DOS function roles; it does not claim original-source names. Name writes preserve the existing namespace; readback returns the fully qualified name used by function inventories. Regular comments map to Ghidra PRE comments and inline comments to EOL comments. Empty text clears a comment; omitted fields preserve it. The whole edit rolls back if a setter, readback or analysis fails.
|
||||
|
||||
```json
|
||||
{
|
||||
"procedure": "0x10100",
|
||||
"name": "entry",
|
||||
"comment": "Recovered startup role",
|
||||
"inline_comment": "Inspect segment setup"
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
rea annotate-native-function /local/program.com 0x10100 --target-format dos-com --provider ghidra --name entry --comment 'Recovered startup role' --json
|
||||
```
|
||||
|
||||
The CLI returns the updated analysis before closing its isolated session. MCP edits remain visible to later reads until `close_binary`. Original bytes remain unchanged, database edits are discarded on close, and immutable analysis snapshots are invalidated after edits. This operation does not save a Ghidra project, change ABI/types/body ranges, patch instructions, or emulate a DOS runtime. Windows P0 does not admit it.
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# Optional NativeAOT metadata recovery
|
||||
|
||||
REA integrates [Washi1337/ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot)
|
||||
through an optional headless adapter. The original source is an opt-in submodule,
|
||||
pinned to `effeb734fc570c32650f88b159608979dc7b423e`; its MIT license and existing
|
||||
source notices are retained. No upstream source or algorithms are rewritten.
|
||||
|
||||
The initial tested layout is .NET runtime **8.0.22**, NativeAOT RTR **9.1**,
|
||||
x86-64 ELF and native Windows PE **targets analyzed on Linux x64**, using
|
||||
Ghidra **12.1.4** and JDK **21**. The build accepts any Ghidra 12.1.x release and
|
||||
javac 21 or newer, and it compiles with `--release 21`. Real verification of
|
||||
this layout remains the Ghidra 12.1.4 and JDK 21 pair. Other NativeAOT layouts,
|
||||
architectures, and hosts are unsupported.
|
||||
Windows Ghidra P0 has no database mutation authority. A PE/CLI or ReadyToRun
|
||||
assembly should use `inspect_managed_artifact`; it is not NativeAOT.
|
||||
|
||||
## Build and configure
|
||||
|
||||
Bring your own supported Ghidra/JDK installation. This developer build needs
|
||||
existing `git`, `javac` and `jar`; it does not invoke Gradle, download toolchains,
|
||||
or change host configuration:
|
||||
|
||||
```sh
|
||||
git submodule update --init third_party/ghidra-nativeaot
|
||||
npm run build:ghidra:nativeaot
|
||||
export REA_GHIDRA_NATIVEAOT_JAR="$PWD/_reference/nativeaot-integration/extension/rea-ghidra-nativeaot.jar"
|
||||
```
|
||||
|
||||
The build requires a clean pinned upstream checkout, compiles unchanged upstream
|
||||
Java plus the REA facade, uses a fresh classes directory, and writes the JAR and
|
||||
build manifest under ignored `_reference/`. `REA_NATIVEAOT_BUILD_ROOT` can select
|
||||
another local output directory; `REA_NATIVEAOT_SOURCE` can select an equivalent
|
||||
clean pinned source checkout. Neither output nor compiled fixture belongs in Git
|
||||
or the npm package. An installed REA accepts an existing explicitly supplied JAR.
|
||||
|
||||
REA commits the actual JAR SHA-256 into the analysis profile, copies identical
|
||||
bytes into the owned runtime, rechecks the digest before loading, and closes the
|
||||
classloader/project on session close. Profiles cannot enable code absent from
|
||||
explicit local configuration. JARs execute within Ghidra; supply a build you trust.
|
||||
The producer's reported source revision is build metadata, not an attestation of
|
||||
arbitrary caller-supplied bytes. The bounded loader accepts regular JARs up to
|
||||
8 MiB to limit startup artifact memory; this does not truncate analysis results.
|
||||
|
||||
## Use existing tools
|
||||
|
||||
Open the executable with provider `ghidra`. `inspect_native_load_image` returns
|
||||
optional `observations.metadata_recovery` inline, including the detected header,
|
||||
layout, recovery status, type category paths/addresses, diagnostics, annotation
|
||||
coverage and derived-memory SHA. The enclosing `unsupported` load-image status
|
||||
for PE/ELF concerns independent DOS loader verification; metadata recovery has
|
||||
its own status. It is not a failed import.
|
||||
|
||||
Pass one returned type path or its typed address to `inspect_native_data_type`.
|
||||
Its optional `metadata_recovery` includes related/base type, implemented
|
||||
interfaces and virtual slots with targets and known procedure names. Follow
|
||||
these with `analyze_function`, string searches and ordinary cross-references.
|
||||
For CLI use:
|
||||
|
||||
```sh
|
||||
rea inspect-native-load-image ./NativeAotFixture --provider ghidra --json
|
||||
rea inspect-native-data-type ./NativeAotFixture --type /NativeAOT/MethodTables/Class_ADDRESS_MT --provider ghidra --json
|
||||
rea function ./NativeAotFixture 0xADDRESS --provider ghidra --json
|
||||
```
|
||||
|
||||
No new MCP workflow or approval flag is required. Configuration opts into the
|
||||
adapter; without it ordinary Ghidra profiles/results remain unchanged.
|
||||
|
||||
## Evidence boundaries
|
||||
|
||||
Discovery prefers an imported RTR symbol, otherwise the upstream initialized,
|
||||
non-executable-memory signature heuristic. REA rejects ambiguous candidates,
|
||||
unknown layout versions, malformed directory rows, unsupported flags and
|
||||
unmapped ranges before recovery. It requires the verified dehydrated layout.
|
||||
|
||||
The adapter performs rehydration, method-table recovery and frozen-object
|
||||
annotation in one ephemeral Program transaction. Fatal failure/cancellation
|
||||
rolls it back. Recovery may change analysis-memory bytes, never the original
|
||||
executable file. Derived ranges have a digest and **no original file offset**;
|
||||
they are not captured runtime state. Candidate/committed instance annotation
|
||||
counts distinguish partial frozen-object coverage. Coverage concerns the
|
||||
recovered candidate set, not every possible object or every runtime type.
|
||||
|
||||
REA preserves pre-recovery calling conventions and uses the loaded compiler
|
||||
specification default for newly created methods. The upstream universal
|
||||
`__thiscall` assignment is not treated as x64 ABI authority. Parameters and
|
||||
method prototypes remain inferences; verify them against instructions/call sites.
|
||||
|
||||
Type relationships and System.Object/System.String identification use upstream
|
||||
heuristics. Generated names are explicitly marked; original class/member names
|
||||
and custom field layouts remain unknown. Pseudocode is recovered native logic,
|
||||
not original C# source. Unsupported recovery reports its stage and reason; it
|
||||
does not imply that ordinary native disassembly/decompilation is impossible.
|
||||
|
||||
## Verification
|
||||
|
||||
See [the dedicated lane](testing.md#optional-nativeaot-ghidra-analysis).
|
||||
Fixtures are source-built benign .NET programs with inheritance, an interface,
|
||||
virtual dispatch, representative arithmetic and frozen strings. Independent
|
||||
compiler/linker symbols and raw directory bytes provide the oracle. Stripped
|
||||
fixtures exercise heuristic detection. Tests do not execute the resulting
|
||||
NativeAOT binaries. Fixture generation and adapter compilation are optional and
|
||||
never prerequisites of ordinary native analysis or setup.
|
||||
@@ -0,0 +1,111 @@
|
||||
# IDA Pro provider
|
||||
|
||||
REA adapts an existing [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) server into its native analysis tools. Install and configure IDA, Hex-Rays, Python, and upstream MCP using that repository's instructions. REA does not install, upgrade, activate, or license them, and does not bundle another IDA server.
|
||||
|
||||
Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP_CONFIG` to that file, then select `ida` once through `REA_ANALYSIS_PROVIDER`, the CLI's `--provider ida`, or MCP's `open_binary.provider_id`. No provider startup occurs during discovery.
|
||||
|
||||
## Analyze an already-open GUI target
|
||||
|
||||
The attached profile supports upstream **1.4.0 legacy tools**, including `get_metadata`, `list_functions`, and `decompile_function`. Copy the upstream registration's `command`, `args`, and optional `env` into a JSON file. An existing `mcpServers` object with an `ida-pro-mcp` entry is also accepted. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "python",
|
||||
"args": [
|
||||
"/absolute/path/to/ida_pro_mcp/server.py",
|
||||
"--ida-rpc",
|
||||
"http://127.0.0.1:13337"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use the executable and arguments from your working upstream registration. The legacy GUI's `/mcp` endpoint is its internal RPC endpoint; connect REA through the upstream stdio proxy rather than treating that URL as Streamable HTTP.
|
||||
|
||||
```bash
|
||||
export REA_IDA_MCP_CONFIG=/absolute/path/to/ida-mcp.json
|
||||
export REA_ANALYSIS_PROVIDER=ida
|
||||
rea function /absolute/path/to/program main --json
|
||||
rea decompile /absolute/path/to/program main --json
|
||||
```
|
||||
|
||||
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows.
|
||||
|
||||
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
|
||||
|
||||
## Give REA a target for headless analysis
|
||||
|
||||
The headless profile supports upstream's **database supervisor** API: `idb_open`, `idb_list`, `idb_close`, explicit `database` arguments, and modern analysis tools. Legacy 1.4.0 `idalib-mcp`, which accepts one initial filename and serves SSE, does not implement this profile. Follow the [upstream headless instructions](https://github.com/mrexodia/ida-pro-mcp#headless-idalib-session-model) for an installation with the supervisor API.
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "idalib-mcp",
|
||||
"args": ["--stdio", "--max-workers", "1"],
|
||||
"mode": "headless"
|
||||
}
|
||||
```
|
||||
|
||||
Use an already-installed executable. When required by your existing idalib installation, include `"env": { "IDADIR": "/absolute/path/to/ida" }`. REA neither edits the global activation configuration nor installs dependencies. A configured local Streamable HTTP supervisor can instead use `"url": "http://127.0.0.1:8745/mcp"` and `"mode": "headless"`.
|
||||
|
||||
The same CLI commands work in both modes. For a Windows-native REA process:
|
||||
|
||||
```powershell
|
||||
$env:REA_IDA_MCP_CONFIG = 'D:\analysis\ida-mcp.json'
|
||||
$env:REA_ANALYSIS_PROVIDER = 'ida'
|
||||
rea function 'D:\samples\program.exe' main --json
|
||||
```
|
||||
|
||||
Headless REA and the configured supervisor must share the host filesystem and native path syntax. Run REA on Windows when the supervisor runs on Windows. The attached profile can span WSL and a Windows GUI because it binds recorded input hashes instead of asking the GUI to open a WSL path.
|
||||
|
||||
REA copies the admitted binary into a private temporary directory, checks its digest, and requests an owned worker with `force_headless`. POSIX workspaces use mode `0700`; Windows uses the matching package's native private-DACL boundary on a local NTFS temporary directory. It injects the returned database ID into analysis requests. It verifies the active owned worker and input path rather than adopting a user database. On close or failed startup, it releases its session with `save: false`, verifies that it is absent from upstream session discovery, and removes its private input and sidecars. An unverified release retains the workspace and reports incomplete cleanup.
|
||||
|
||||
One REA session owns one database. Stdio registrations default `IDA_MCP_MAX_WORKERS` to `1`; the explicit upstream `--max-workers 1` also bounds a supervisor. Database size and engine memory depend on the target and upstream; REA does not impose an engine memory limit. The original binary is never executed or overwritten. Optional `workspaceRoot` selects an existing absolute temporary-directory parent on the REA host. Ensure it has space for the input copy and IDA working files.
|
||||
|
||||
Headless cleanup uses the upstream database lifecycle. Abruptly terminating REA, its proxy, or the supervisor does not establish worker release. Prefer `close_binary` for MCP sessions; after an interrupted process, inspect upstream session inventory and retained workspace diagnostics before removing working files. This adapter does not claim Windows Job Object containment for the upstream detached workers.
|
||||
|
||||
## Connect an agent once
|
||||
|
||||
Add the registration path to the **REA** MCP entry's environment, keeping the upstream registration in its separate JSON file:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@<version-with-ida>", "mcp"],
|
||||
"env": {
|
||||
"REA_ANALYSIS_PROVIDER": "ida",
|
||||
"REA_IDA_MCP_CONFIG": "/absolute/path/to/ida-mcp.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use a package version that actually includes this adapter; repository main can be ahead of the npm release. After reconnecting, call:
|
||||
|
||||
```json
|
||||
{"name":"open_binary","arguments":{"path":"/absolute/path/to/program","provider_id":"ida"}}
|
||||
{"name":"analyze_function","arguments":{"procedure":"main"}}
|
||||
{"name":"search_strings","arguments":{"pattern":"license"}}
|
||||
{"name":"close_binary","arguments":{}}
|
||||
```
|
||||
|
||||
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
|
||||
|
||||
For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <client>`. Setup includes that file reference in its reviewed plan and preserves it after approval; it does not copy upstream credentials into the agent configuration. `rea doctor --provider ida --json` validates the registration without starting IDA and explicitly leaves runtime compatibility and target binding for analysis.
|
||||
|
||||
## Coverage and evidence
|
||||
|
||||
Both profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
|
||||
|
||||
The legacy profile additionally supports direct callers. Its producer may report call-site or interior addresses; REA resolves the reported function symbol to its canonical entry. The modern profile does not prove direct callers from its code/data xref labels, so that capability is unavailable and the dossier marks it unknown.
|
||||
|
||||
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. `binary_overview`, whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
|
||||
|
||||
`provider.version` identifies the REA adapter; the profile explicitly records `version_scope: "rea-ida-adapter"`. The MCP handshake's `serverInfo` is separate and does not establish IDA or Python distribution versions. Raw observations preserve producer metadata and output. Attached hashes identify the recorded original input; headless identity uses the digest-verified private copy and observed worker input path. Neither proves source-code recovery or an unmodified analysis database.
|
||||
|
||||
## Verification
|
||||
|
||||
Run `npm run verify:ida -- --target /absolute/path/to/program --procedure main` with a configured upstream server. It exercises production CLI and MCP tools, validates their output contracts and Evidence, checks original-input preservation and session cleanup, and keeps local target data out of its console summary. Supply `--package-root` to verify an extracted or installed package. See [testing](testing.md) for the distinction between adapter tests and real-provider evidence.
|
||||
|
||||
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux/macOS headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: REA
|
||||
text: Reverse Engineer Anything
|
||||
tagline: Understand binaries, applications, and runtime behavior with one CLI and MCP server.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get started
|
||||
link: /installation
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/morluto/rea
|
||||
|
||||
features:
|
||||
- title: Investigate with your agent
|
||||
details: Connect REA to your coding agent, describe the app or feature you want to understand, and follow the evidence.
|
||||
link: /mcp-prompts
|
||||
- title: Inspect across formats
|
||||
details: Explore native binaries, JavaScript and Electron apps, .NET assemblies, Android packages, firmware, and websites.
|
||||
link: /installation
|
||||
- title: Keep the evidence
|
||||
details: Analysis runs locally and returns observations, limitations, and unknowns through the CLI and MCP.
|
||||
link: /mcp-contracts
|
||||
---
|
||||
|
||||
## Start with one command
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Choose your agent, review the planned changes, and approve setup. Native binary
|
||||
analysis can use an existing Hopper, Ghidra, or IDA installation; static
|
||||
JavaScript analysis needs no native analysis engine.
|
||||
|
||||
For a first result from an extracted JavaScript application or ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
See [installation and setup](./installation.md) for requirements and configuration.
|
||||
|
||||

|
||||
+379
-37
@@ -36,7 +36,7 @@ Setup continues to pin persistent MCP registrations to the exact version that
|
||||
performed setup. Running current setup later migrates unversioned or older
|
||||
managed registrations through the normal reviewed setup transaction.
|
||||
|
||||
REA supports Node.js 22.19+ and 24.11+ (including newer releases). It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
REA supports Node.js 22.x (>=22.19), 24.x (>=24.11), and 26+. Node.js 23, 25, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
|
||||
Running `npm install rea-agents` without `--global` installs the executable only
|
||||
in the current project's `node_modules/.bin`; it does not make `rea` available
|
||||
@@ -61,9 +61,66 @@ curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh |
|
||||
|
||||
Supported options are `--version <semver>`, `--dry-run`, `--no-setup`, `--no-prompt`, and `--verbose`. Neither `--no-prompt` nor a non-interactive shell grants permission to install external dependencies.
|
||||
|
||||
## Released package and main
|
||||
|
||||
Repository main documents its current code and generated catalog. `@latest`
|
||||
selects the npm release, and persistent MCP registrations are pinned to the
|
||||
version that performed setup. Installing newer instructions does not update a
|
||||
running server or its registration.
|
||||
|
||||
The release checked on **2026-10-07** was **5.0.0** (133 MCP tools), published
|
||||
from the fixed checkpoint
|
||||
[`b33236ec`](https://github.com/morluto/rea/releases/tag/rea-agents-5.0.0).
|
||||
The public CLI, MCP catalog and target-free session, and isolated update from
|
||||
4.1.0 to 5.0.0 were verified through npm. The artifact includes Windows native
|
||||
controls, Android/JADX and firmware tools, Ghidra function annotations, and
|
||||
retained application-Evidence references.
|
||||
|
||||
Main's catalog describes the current code. A source build or a subsequent
|
||||
release containing changes after this checkpoint is required for newer
|
||||
functionality. Package startup alone does not verify a provider's real
|
||||
platform workflow.
|
||||
|
||||
To check the published version, run `npm view rea-agents dist-tags.latest`.
|
||||
Use the connected server's actual tool list and advertised input schemas for
|
||||
feature selection. The same package version string in a development checkout
|
||||
does not establish that its bytes match the npm tarball. Update a registration
|
||||
through a reviewed, scoped setup plan, then restart/reconnect the agent.
|
||||
|
||||
## Skill-only installation
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
This installs agent instructions and bundled references, not REA MCP
|
||||
registration or analysis engines. Follow the skill's
|
||||
[conditional connection guide](https://github.com/morluto/rea/blob/main/skill-src/reverse-engineer-anything/SKILL.md#connect-only-when-needed).
|
||||
Working tools can be used immediately. If tools are missing, inspect the current
|
||||
client's registration with `doctor --client codex --json` (substitute its client
|
||||
ID), then plan repairs with `setup --client codex --dry-run --json`. Show and
|
||||
approve the exact changes before applying that scope. An aligned registration
|
||||
with tools absent from the active session needs a restart/reconnection;
|
||||
`doctor` checks files and prerequisites, not the live agent connection.
|
||||
|
||||
Guided setup installs the package's matching skill by default. The skills.sh
|
||||
route can select newer repository instructions, so follow actual server schemas
|
||||
and the release boundary above. Static JavaScript CLI inspection can proceed
|
||||
while MCP is unavailable:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
It returns the complete Evidence record directly and requires no native engine.
|
||||
Provider failures in doctor do not prevent unrelated target-free tools. To check
|
||||
readiness for one task instead of auditing every integration, see
|
||||
[Check readiness for your task](#check-readiness-for-your-task).
|
||||
|
||||
## Supported agents
|
||||
|
||||
Setup can configure these clients for REA's local MCP server:
|
||||
Setup can configure these clients for REA's local MCP server. Grok Bot is
|
||||
listed after the table because its connector is not one of these files:
|
||||
|
||||
| Client | `--client` value |
|
||||
| ------------------ | ---------------- |
|
||||
@@ -77,7 +134,38 @@ Setup can configure these clients for REA's local MCP server:
|
||||
| OpenCode | `opencode` |
|
||||
| Antigravity | `antigravity` |
|
||||
| GitHub Copilot CLI | `copilot_cli` |
|
||||
| Command Code | `commandcode` |
|
||||
| VS Code | `vscode` |
|
||||
| Grok Build | `grok_build` |
|
||||
|
||||
For OpenCode, setup writes the V1 `mcp.rea` entry, which OpenCode V1 and V2
|
||||
both load. If the configuration already uses OpenCode V2's native
|
||||
`mcp.servers` table, setup registers REA there instead and replaces any earlier
|
||||
`mcp.rea` entry from REA.
|
||||
|
||||
Grok Build loads `[mcp_servers.rea]` from `$GROK_HOME/config.toml`, or from
|
||||
`~/.grok/config.toml` when `GROK_HOME` is unset. Setup edits that server
|
||||
table, `[mcp_servers.rea.env]`, and a root `disabled_mcp_servers` entry that
|
||||
names `rea`. It sets `startup_timeout_sec = 30` and leaves every other name
|
||||
in that list. The shared skill installed under `~/.agents/skills` is already
|
||||
on Grok Build's skill path.
|
||||
|
||||
Grok Bot (`grok_bot`) is detected from `~/.grokbot`, or from `SAND_DATA_ROOT`
|
||||
when that value is an absolute path. A relative or empty `SAND_DATA_ROOT`
|
||||
stays on `~/.grokbot`. That directory is not the connector store. Grok Bot
|
||||
keeps connectors in the signed-in account and runs them on its hosted
|
||||
computer. It does not import `mcp.json` from the data directory, and it does
|
||||
not attach a stdio server running on this machine. Setup does not call the
|
||||
account connector API, does not write a registration file, and does not report
|
||||
the data directory as aligned. Ask the Grok Bot chat to add a custom MCP
|
||||
server named `rea` that runs on the Bot's computer:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@<version> mcp
|
||||
```
|
||||
|
||||
Do not put credentials in that command or its arguments. `rea doctor --client grok_bot`
|
||||
reports this manual step. `rea uninstall` does not remove the account connector.
|
||||
|
||||
## Review setup changes
|
||||
|
||||
@@ -107,8 +195,9 @@ Before applying changes, REA checks your current configuration. The plan lists:
|
||||
|
||||
Malformed or unsafe existing configuration blocks the whole transaction before
|
||||
Hopper installation or any file write. Declining or pressing Ctrl-C makes no
|
||||
changes. Agent configuration writes preserve unrelated
|
||||
entries, create backups, use atomic replacement, and verify their result.
|
||||
changes. Agent configuration writes preserve unrelated entries and comments,
|
||||
create backups, use atomic replacement, and verify their result. Setup and
|
||||
uninstall retain an existing `.rea.backup` rather than replacing the first snapshot.
|
||||
|
||||
After setup, REA reports which agents, analysis tools, and workflow files passed
|
||||
its final checks. Restart any agent named in the completion message, then begin
|
||||
@@ -147,7 +236,7 @@ rea setup --yes --all-detected --install-hopper --json
|
||||
|
||||
Setup pins package-runner MCP registrations to the exact installed REA version,
|
||||
installs the matching skill and on-demand references in the same plan, and adds
|
||||
`startup_timeout_sec = 30` for Codex. `rea update` installs the exact resolved
|
||||
`startup_timeout_sec = 30` for Codex and Grok Build. `rea update` installs the exact resolved
|
||||
release into the npm prefix that owns the running package, then checks the new
|
||||
executable's version before reporting success. It does not reopen onboarding.
|
||||
Release lookup and installation both use npm's configured registry.
|
||||
@@ -156,12 +245,44 @@ installed REA skill. Run the returned scoped setup command to review and approve
|
||||
those changes, then restart affected agents. The plan is returned in terminal,
|
||||
non-TTY, and JSON modes without applying configuration changes.
|
||||
|
||||
## Check readiness for your task
|
||||
|
||||
Run `rea doctor` when you need diagnosis. It reads host prerequisites and agent
|
||||
configuration without changing them. Without options, it audits every detected
|
||||
registration, the installed skill and optional analysis engines; its overall
|
||||
`healthy` value can be false while your chosen workflow works.
|
||||
|
||||
Select the part you want to check:
|
||||
|
||||
| Task | Readiness check |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------ |
|
||||
| Static JavaScript/Electron analysis | Run `rea analyze-javascript-application PATH --json` directly. |
|
||||
| One analysis engine | `rea doctor --provider ghidra --json` (or `hopper`, `ida`) |
|
||||
| One agent registration | `rea doctor --client codex --json` (see [client IDs](#supported-agents)) |
|
||||
| Installed workflow instructions | `rea doctor --skill --json` |
|
||||
|
||||
A scoped report has `scope.mode: "explicit"`. Its `scope_checks` determine
|
||||
`healthy` and the exit status. Other checks appear in `informational_checks`;
|
||||
`environment_healthy` summarizes the full audit. `--target` adds a target check,
|
||||
but the report remains audit-wide unless a scope option is also supplied.
|
||||
|
||||
Follow the remediation for the failed check. If a native target has several
|
||||
available providers, choose one with `--provider` on the CLI or `provider_id`
|
||||
on `open_binary`. A `capability_unavailable` failure carries
|
||||
`details.selection_reason`: `ambiguous` asks you to choose from
|
||||
`details.candidate_ids`, while `provider_unavailable` asks you to repair the
|
||||
selected engine. See [provider selection](cli.md#choose-a-provider).
|
||||
|
||||
## Hopper
|
||||
|
||||
Hopper is separate commercial software with its own license. Its free demo has
|
||||
vendor-defined limits, and a paid license is optional. REA reuses any detected
|
||||
installation and preserves Hopper during uninstall.
|
||||
|
||||
The supported native host baseline is macOS 12+, Ubuntu 24.04+, Fedora 41+,
|
||||
64-bit Arch Linux, or CachyOS. Ghidra and IDA have their own provider-specific
|
||||
host requirements; Windows Ghidra uses the [experimental P0 boundary](windows-ghidra-p0.md).
|
||||
|
||||
On macOS, approved setup downloads the official DMG, checks its published size
|
||||
and digest, validates the application bundle, and atomically installs it to
|
||||
`~/Applications/Hopper Disassembler.app`. REA then opens Hopper so the
|
||||
@@ -190,6 +311,102 @@ host mount and the rest of `/tmp` remain unchanged; this fallback never invokes
|
||||
`sudo`. `rea doctor --provider hopper --json` reports the selected
|
||||
strategy and both host and effective mount facts.
|
||||
|
||||
The supported Linux demo forwards additional launches into its existing
|
||||
application, even across private displays. REA therefore reserves one Linux
|
||||
Hopper application per user across cooperating REA processes. A competing
|
||||
CLI or MCP session receives the owning session ID before Hopper is launched,
|
||||
for both the same target and a different target. Switch targets through that
|
||||
MCP session, or close it before starting another session. Closing the owner
|
||||
releases the reservation. This does not attach to or claim ownership of
|
||||
manually opened Hopper applications.
|
||||
|
||||
Stale lease recovery is serialized. If an interrupted recovery leaves its
|
||||
reservation directory, the diagnostic identifies that exact path. Stop REA
|
||||
sessions before removing that recovery directory and retrying.
|
||||
|
||||
### Launcher paths and troubleshooting
|
||||
|
||||
On macOS, REA uses
|
||||
`/Applications/Hopper Disassembler.app/Contents/MacOS/hopper` by default.
|
||||
On Linux, it prefers executable `/opt/hopper/bin/Hopper`, then executable
|
||||
`~/.local/share/rea/hopper/bin/Hopper`; if neither exists, the former remains
|
||||
the diagnostic fallback. `HOPPER_LAUNCHER_PATH` overrides those choices:
|
||||
|
||||
```bash
|
||||
export HOPPER_LAUNCHER_PATH=/absolute/path/to/Hopper
|
||||
rea doctor --provider hopper --json
|
||||
```
|
||||
|
||||
If a Linux launcher exists but cannot start, inspect its shared libraries:
|
||||
|
||||
```bash
|
||||
ldd /absolute/path/to/Hopper | grep 'not found'
|
||||
```
|
||||
|
||||
Install the missing packages and rerun the scoped check. Linux demo sessions
|
||||
need Xvfb, Python 3, X11 and XTEST; approved setup installs those dependencies
|
||||
on supported distributions. Add the curl installer's reported executable
|
||||
directory, commonly `~/.local/bin`, to your shell `PATH` if needed.
|
||||
|
||||
REA starts Hopper when needed. On macOS, its launcher can bring a window or
|
||||
first-run dialog forward; choose demo mode or activate your existing license.
|
||||
Hopper serializes analysis requests. Cancelling a wait can leave provider work
|
||||
running, which the session reports. Successful decompilation is cached until
|
||||
a relevant rename or comment changes it.
|
||||
|
||||
Analysis and annotation calls stay bound to the active target's native Hopper
|
||||
document, even when GUI focus changes or other documents have the same display
|
||||
name. Use `open_binary` to change targets. Byte reads stop at a segment boundary
|
||||
and return the readable prefix with `complete: false`. File-offset mapping checks
|
||||
the reverse lookup and original executable bounds; synthetic external-symbol
|
||||
memory has no original file offset. For FAT Mach-O, offsets refer to the original
|
||||
container file. Results retain Hopper's image-relative offset, the observed slice
|
||||
base, and the source executable path.
|
||||
Loader selection reads the Mach-O container header, including FAT files with a
|
||||
single architecture. For FAT64, REA validates the architecture table and selected
|
||||
Mach-O header, prepares a private thin image, and loads it with Hopper's native
|
||||
Mach-O loader. It checks the entire source's SHA-256 while copying the slice;
|
||||
source identity and reported offsets still refer to the original container.
|
||||
An ambiguous architecture subtype requires an explicitly extracted thin image;
|
||||
REA does not guess. Configured loader arguments remain explicit overrides.
|
||||
FAT64 is distinct from the CPU architecture: FAT32 containers can contain 64-bit
|
||||
executable images. This preparation avoids the Raw Binary loader dialog observed
|
||||
with native FAT64 loading on Hopper 6.1.0-demo.
|
||||
The prepared image and its owned document close together, including on MCP exit;
|
||||
if document closure is unconfirmed, REA retains the backing image and reports
|
||||
`cleanup_incomplete` with its path. Ordinary documents retain their existing
|
||||
MCP-exit behavior.
|
||||
Startup deadlines report missing bridge readiness and preserve the launcher
|
||||
outcome; a successful helper exit does not prove that a loader dialog completed.
|
||||
Cursor navigation returns the observed object start when Hopper snaps an interior
|
||||
address; adjacent-object navigation rejects unmapped inputs and document ends.
|
||||
Native API text rejects NUL characters and unpaired Unicode surrogates before
|
||||
annotation changes. Renames preserve unselected label owners; use a batch with
|
||||
all affected addresses to move or swap existing labels explicitly. Every rename
|
||||
destination must be mapped, and native symbol names must fit Hopper's 1024 UTF-16
|
||||
code-unit limit. Oversized names fail before any batch edits; bookmarks and
|
||||
literal string results are not subject to that symbol-name limit. Rename success
|
||||
requires exact final readback. New bookmarks must point into mapped memory;
|
||||
existing legacy bookmarks outside it can still be removed.
|
||||
String results read each native typed object's complete bytes, retain the original
|
||||
provider display in `provider_value`, and report its encoding, byte length, and
|
||||
termination. `encoding_status: inferred` distinguishes REA's decoding from an
|
||||
observed source encoding. Hopper can split long literals into adjacent
|
||||
unterminated objects; search matches each object's decoded bytes independently.
|
||||
Undecodable objects retain native display text with `decoding.available: false`
|
||||
and a reason, so one uncertain object does not block unrelated inspections.
|
||||
Function dossiers retain this same string evidence. Native call edges retain
|
||||
Hopper's partial `CallReference` classification and exact endpoints; detailed
|
||||
reference flags remain unavailable rather than being invented.
|
||||
Regex searches use ECMAScript Unicode syntax in a cancellable worker with a
|
||||
five-second matching deadline. Deadline or cancellation stops matching while
|
||||
leaving the Hopper API available. Literal mode retains Unicode casefold matching.
|
||||
|
||||
Closing or switching a target closes its bound Hopper document, shuts down REA's
|
||||
bridge and removes its temporary socket directory while preserving the Hopper
|
||||
application and unrelated documents. A `cleanup_incomplete`
|
||||
result identifies resources whose cleanup could not be verified.
|
||||
|
||||
### Hopper in CI
|
||||
|
||||
REA's unattended Linux path is validated against Hopper's offered demo mode.
|
||||
@@ -212,22 +429,34 @@ copying license secrets into logs, or killing unrelated Hopper processes.
|
||||
|
||||
## Ghidra
|
||||
|
||||
REA connects to an existing Ghidra installation on Linux x64 or macOS x64/arm64.
|
||||
It requires Ghidra 12.1.4 and a 64-bit full JDK 21. On macOS, the installation
|
||||
must include the native decompiler for the host architecture; REA does not
|
||||
build it or change Gatekeeper quarantine settings.
|
||||
REA connects to an existing Ghidra installation on Linux x64/arm64, macOS x64/arm64,
|
||||
or experimental Windows x64 P0.
|
||||
It accepts Ghidra 12.1.x and the 64-bit full JDK declared by that installation's
|
||||
`application.java.min` and `application.java.max`. Current 12.1 releases require
|
||||
JDK 21 or newer and set no maximum. The bridge is verified with Ghidra 12.1.4
|
||||
and JDK 21. Each installation must include the native decompiler for the host
|
||||
architecture in `Ghidra/Features/Decompiler/os/<platform>/` or the corresponding
|
||||
`build/os/<platform>/` directory. Linux ARM64 uses `linux_arm_64`; official
|
||||
release archives may require you to build that native component separately.
|
||||
REA checks the executable prerequisite and does not build or install native
|
||||
tools, or change Gatekeeper quarantine settings.
|
||||
|
||||
The adapter exposes 22 read-only operations: ten inventory/name/search
|
||||
The adapter exposes 25 read-only operations: thirteen inventory/name/search
|
||||
operations and twelve function-analysis operations. These cover metadata,
|
||||
decompilation, assembly, resolved calls, typed references, xrefs, function
|
||||
dossiers, instructions, and recovered data types. GUI controls and annotation
|
||||
changes require Hopper.
|
||||
dossiers, instructions, recovered data types, measured load mappings, loaded
|
||||
memory bytes, and observed file offsets. Independent load-image attestation
|
||||
supports DOS MZ and explicitly selected COM; PE returns its measurements with that limitation.
|
||||
On Linux and macOS, `annotate_native_function` also edits a function name and/or
|
||||
entry comments atomically and returns refreshed analysis. These session metadata
|
||||
edits leave executable bytes unchanged and are discarded on close. GUI controls
|
||||
require Hopper; Windows P0 remains read-only.
|
||||
|
||||
Windows Ghidra operations are currently unavailable. The adapter reports
|
||||
`unsupported_host` until verified Job Object process ownership, private runtime
|
||||
DACLs, and reparse-safe path admission are implemented. The
|
||||
[Windows Ghidra P0 guide](windows-ghidra-p0.md) describes the intended boundary
|
||||
and remaining controls.
|
||||
Windows P0 admits native x86 and x86-64 PE applications on fixed local NTFS volumes.
|
||||
The npm package bundles native Job Object ownership, protected private runtime
|
||||
DACLs, and handle-based path admission; no separate addon installation is needed.
|
||||
See the [Windows Ghidra P0 guide](windows-ghidra-p0.md) for verified scope and
|
||||
limitations.
|
||||
|
||||
Extract Ghidra and install the JDK outside REA, then export absolute paths:
|
||||
|
||||
@@ -238,7 +467,7 @@ rea doctor --json
|
||||
rea setup
|
||||
```
|
||||
|
||||
For Windows diagnostics, use the same installation paths in PowerShell. These settings do not enable the blocked analysis operations:
|
||||
On Windows, configure the existing installation in PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.4_PUBLIC"
|
||||
@@ -251,12 +480,12 @@ rea providers --json
|
||||
REA skill after approval. Direct registrations use Node to launch REA's entry
|
||||
script; package-runner registrations use the pinned `npx` command. Hopper
|
||||
installation remains unavailable on Windows. Setup never installs Ghidra,
|
||||
Java, or Python, and agent registration does not enable blocked Ghidra
|
||||
operations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagnostics.
|
||||
Java, or Python. It preserves valid detected Ghidra/JDK settings in agent
|
||||
registrations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagnostics.
|
||||
|
||||
Doctor validates the platform, architecture, application version,
|
||||
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, Java
|
||||
version/bitness, and the presence of `javac`/`javac.exe`.
|
||||
Doctor validates the platform, architecture, Ghidra 12.1.x application version,
|
||||
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, the installation's
|
||||
Java major range, 64-bit JDK bitness, and the presence of `javac`/`javac.exe`.
|
||||
When Java is found through `PATH`, setup records its observed JDK home so GUI
|
||||
MCP clients do not depend on an incidental shell path. Setup shows every exact
|
||||
environment entry in its plan, writes only after approval, and never downloads,
|
||||
@@ -264,15 +493,39 @@ installs, upgrades, or modifies Ghidra or Java.
|
||||
|
||||
Each verified session uses an ephemeral temporary project and isolated
|
||||
home/cache/config/temp paths. REA passes `-readOnly`, `-deleteProject`, uses
|
||||
Ghidra's default analysis and resource settings, and loads its packaged Java
|
||||
Ghidra's default analysis settings, and loads its packaged Java
|
||||
bridge via `-scriptPath`; it never opens an existing user project. Linux and
|
||||
macOS use a current-user-only local bridge socket and descriptor. The
|
||||
experimental Windows transport uses authenticated IPv4 loopback, but missing
|
||||
native ownership and path controls keep Windows operations unavailable.
|
||||
project remains under the selected temporary directory. If its Unix socket
|
||||
pathname would exceed the host's byte limit, REA allocates a separate mode-0700
|
||||
socket directory under `/tmp` and removes it on close, cancellation, or failure.
|
||||
Diagnostics retain the actual endpoint and both owned directories.
|
||||
On Linux and macOS, REA starts the inspected JVM directly using Ghidra's own LaunchSupport
|
||||
configuration. Apple platform shell wrappers hide their environments from
|
||||
ownership inspection, so retaining those wrappers would prevent verified
|
||||
process-group cancellation during startup.
|
||||
If ownership remains unverifiable, it reports the reason and retains the process
|
||||
supervisor and private runtime instead of removing files beneath a live provider.
|
||||
The experimental Windows transport uses authenticated IPv4 loopback with a
|
||||
private native-owned bearer descriptor and Job Object process ownership.
|
||||
|
||||
Operations begin only after default auto-analysis completes. The provider
|
||||
startup deadline fails the open rather than exposing partial analysis. One
|
||||
session contains exactly one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
|
||||
Operations begin only after default auto-analysis completes. `open_binary`
|
||||
selects and validates the target/provider binding; it does not wait for Ghidra
|
||||
import and auto-analysis. The first Ghidra-backed query starts that work lazily.
|
||||
The provider startup deadline is 330,000 ms by default for import, analysis,
|
||||
bridge, and health readiness. Large binaries can need more: set
|
||||
`REA_GHIDRA_STARTUP_TIMEOUT_MS` to an integer between 1 and 2,147,483,647
|
||||
milliseconds in the server environment. An absent setting uses the default;
|
||||
empty, invalid or out-of-range supplied values fail configuration validation.
|
||||
REA parses the supplied configuration environment and
|
||||
passes the deadline to each provider client; a startup failure is returned by the query that triggered it,
|
||||
without exposing partial analysis. This deadline is separate from MCP transport
|
||||
initialization and the client's deadline for that individual tool call. A client
|
||||
can time out earlier even when Ghidra would complete within its startup deadline.
|
||||
See [Ghidra first-query deadlines and recovery](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
|
||||
for client options and the close/reopen recovery flow.
|
||||
|
||||
One session contains exactly one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
|
||||
`REA_ANALYSIS_PROVIDER=ghidra` when both Hopper and Ghidra support the target.
|
||||
One persistent decompiler is owned by the Program, and a serial queue keeps
|
||||
Ghidra API calls on the owning Program thread without a fixed queue length.
|
||||
@@ -281,8 +534,75 @@ is no fixed per-operation or response-size ceiling. Unresolved computed calls
|
||||
remain unknown, reference-kind provenance is preserved, and provider-specific
|
||||
pseudocode is never treated as original source or Hopper-equivalent text.
|
||||
|
||||
### Ghidra heap and CPU controls
|
||||
|
||||
Set resource controls in the environment that launches the CLI or MCP server,
|
||||
before opening a new Ghidra session. On Linux and macOS, REA chooses the first
|
||||
nonempty setting in this order: `GHIDRA_HEADLESS_MAXMEM`, `GHIDRA_MAXMEM`, then
|
||||
`2G`. The chosen value becomes the JVM's `-Xmx` argument. Use a JVM heap-size
|
||||
value such as `512M` or `2G`; an invalid value causes Java startup to fail.
|
||||
The supported Ghidra 12.1.4 Windows headless script consumes the same settings
|
||||
with the same precedence.
|
||||
|
||||
For a small target on a constrained host:
|
||||
|
||||
```bash
|
||||
export GHIDRA_HEADLESS_MAXMEM=512M
|
||||
```
|
||||
|
||||
In PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:GHIDRA_HEADLESS_MAXMEM = "512M"
|
||||
```
|
||||
|
||||
An MCP client must pass this setting to its REA server process; changing an
|
||||
unrelated terminal's environment does not reconfigure an existing server or
|
||||
JVM. A 512 MiB heap was verified with small native fixtures, but larger targets
|
||||
can require more memory. The Java heap limit does not bound JVM RSS, Node's
|
||||
heap, or total memory used by the process family. The startup deadline controls
|
||||
waiting time independently of these memory settings.
|
||||
|
||||
REA clears `_JAVA_OPTIONS`, `JAVA_TOOL_OPTIONS`, `JDK_JAVA_OPTIONS`, and
|
||||
`GHIDRA_JAVA_OPTIONS` inherited from the caller, and owns
|
||||
`GHIDRA_HEADLESS_JAVA_OPTIONS`. Custom flags injected through these variables
|
||||
are not supported configuration. On Windows, REA replaces `JDK_JAVA_OPTIONS`
|
||||
with its own isolated home/temp settings and `-XX:-UsePerfData`; on Linux and
|
||||
macOS it supplies isolated paths directly to Java. Use the supported heap
|
||||
settings above instead of adding a second `-Xmx` through an injection variable.
|
||||
|
||||
The Linux/macOS launch sets `-XX:ParallelGCThreads=2` and
|
||||
`-XX:CICompilerCount=2`; the supported Windows headless script also sets these
|
||||
counts. These constrain particular JVM thread pools, not all analysis threads
|
||||
or CPU usage. REA does not pass Ghidra's `-max-cpu` option or expose a general
|
||||
Ghidra CPU limit. When needed, apply host CPU affinity and scheduling controls
|
||||
to the REA process and verify the resulting JVM's affinity. Select CPUs allowed
|
||||
on the host; do not assume CPU numbers from another machine are valid.
|
||||
|
||||
Verify controls at their consumer after the first Ghidra-backed query starts
|
||||
the JVM: inspect that owned JVM's effective `-Xmx` and GC/compiler flags, CPU
|
||||
affinity, and observed memory use. Do not infer them from the parent shell's
|
||||
environment or the absence of an error. On Linux, `/proc/<jvm-pid>/cmdline`
|
||||
contains the launch arguments and `/proc/<jvm-pid>/status` reports
|
||||
`Cpus_allowed_list` and `VmRSS`. Inspect only the process belonging to this
|
||||
session and retain the resource flags needed for the measurement rather than
|
||||
copying full command lines or environments into reports. Heap and affinity
|
||||
settings affect cost and latency; they are not part of REA's semantic analysis
|
||||
profile identity. See [real-provider testing](testing.md) for bounded
|
||||
verification workflows.
|
||||
|
||||
### Ghidra diagnostics and verification
|
||||
|
||||
Unexpected request failures retain their original internal cause. CLI and MCP
|
||||
errors expose Error names, messages, and available codes under
|
||||
`details.diagnostics.failure_cause`; other rejection values retain their
|
||||
primitive value or an explicit type. Shutdown warnings include the failure
|
||||
kind, message, and diagnostics, while process and temporary-project cleanup
|
||||
continues. These messages redact known bridge authentication tokens and
|
||||
preserve local paths and other analysis context.
|
||||
|
||||
Run `GHIDRA_INSTALL_DIR=... npm run verify:ghidra` from a source checkout to
|
||||
compile and analyze debug and stripped host-native fixtures (ELF on Linux x64
|
||||
compile and analyze debug and stripped host-native fixtures (ELF on Linux x64/arm64
|
||||
or Mach-O on macOS), plus a native DWARF 4 type-layout object. This lane needs a
|
||||
host C compiler in addition to Ghidra and its JDK.
|
||||
|
||||
@@ -295,14 +615,31 @@ host-native Ghidra acceptance lane.
|
||||
On a controlled Windows x64 runner, use
|
||||
`npm run verify:ghidra:windows`. The verifier generates a deterministic native
|
||||
PE fixture from source bytes and requires the Windows native authority before
|
||||
opening the provider. This lane remains blocked until those controls are
|
||||
implemented; its intended checks include operation coverage, digest identity,
|
||||
and complete runtime cleanup.
|
||||
opening the provider. The controls are implemented on main; see the
|
||||
[release boundary](#released-package-and-main). The lane verifies all 25 admitted
|
||||
read-only operations, digest identity, and runtime cleanup. The separate
|
||||
`verify:ghidra:windows:package` lane checks the packed CLI/MCP boundary with an
|
||||
ordinary user. It requires the built Windows native bundle and an existing
|
||||
Ghidra/JDK installation; neither is inferred from startup alone.
|
||||
|
||||
## Diagnose, update, and remove
|
||||
|
||||
`rea doctor --json` is strictly read-only. `rea update` updates only the npm installation that owns the running CLI. Source checkouts and package-runner copies must be updated through the mechanism that owns them; a fresh package-runner invocation can use `npx rea-agents@latest`. `rea uninstall` removes only REA-owned agent registrations and skill files; `--purge-data` additionally removes REA cache and state paths.
|
||||
|
||||
```bash
|
||||
rea update
|
||||
rea uninstall
|
||||
rea uninstall --purge-data
|
||||
```
|
||||
|
||||
Uninstall preserves Hopper, Node.js, Evidence files, captures, unrelated skills
|
||||
and other MCP servers. Purging removes only REA's cache and state under
|
||||
`~/.rea`. A client configuration that is malformed, unreadable, or at an unsafe
|
||||
path stops the operation before anything is removed, and a client that fails
|
||||
while being updated stops the remaining removals. A purge path that is a
|
||||
symbolic link is retained and reported rather than followed. See the
|
||||
[CLI guide](cli.md#output-and-exit-status) for exit statuses.
|
||||
|
||||
## MCP Registry
|
||||
|
||||
REA is published in the official MCP Registry as `io.github.morluto/rea`. Registry
|
||||
@@ -315,17 +652,22 @@ server through `npx` with the `mcp` command.
|
||||
|
||||
For a client that requires manual configuration, use:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@latest", "mcp"]
|
||||
"args": ["-y", "rea-agents@6.1.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use an exact `rea-agents@VERSION` in the arguments when a reproducible client
|
||||
configuration is required. `rea setup` writes the same package-runner shape and
|
||||
pins it to the exact version that performed setup.
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
Persistent registrations should use one exact package version. `rea setup`
|
||||
writes the same package-runner shape and pins it to the exact version that
|
||||
performed setup. Run current setup to refresh an older registration, then
|
||||
restart the client.
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
|
||||
REA derives a complete reachable feature trace from one authenticated JavaScript
|
||||
Application Graph and compares two authenticated graph versions. The MCP tools
|
||||
are `trace_application_feature`, `trace_javascript_semantics`,
|
||||
`compare_application_versions`, `compare_source_to_bundle`, and
|
||||
`compare_javascript_export_shapes`; their CLI equivalents use the same names
|
||||
with hyphens.
|
||||
are `inspect_analysis_view`, `trace_application_feature`,
|
||||
`trace_javascript_semantics`, `compare_application_versions`,
|
||||
`compare_source_to_bundle`, and `compare_javascript_export_shapes`; their CLI
|
||||
equivalents use the same names with hyphens. `inspect_analysis_view` projects a
|
||||
summary, module page, or one module from already completed application
|
||||
Evidence; it does not walk relationships.
|
||||
|
||||
These workflows consume Evidence produced by
|
||||
`inspect_analysis_view` also projects retained `inspect_binary_layout`
|
||||
Evidence. The remaining workflows consume Evidence produced by
|
||||
`analyze_javascript_application` or `reconcile_javascript_runtime`. They do not
|
||||
read an artifact, execute application code, attach to a process, or open a
|
||||
native-analysis provider. Static artifact observations, passive runtime
|
||||
@@ -55,6 +58,16 @@ registrations/removals and dispatch, Node timers and cancellation handles, async
|
||||
defaults, request construction and response consumers, parse/coercion/
|
||||
validation boundaries, and built-in resource acquisition/release.
|
||||
|
||||
Mutation tracking covers explicit member assignments, updates, deletion, and
|
||||
loop assignment targets, including the supported local aliases and shared
|
||||
nested references. Calls such as `Object.assign`, `Reflect.set`, and
|
||||
`Reflect.deleteProperty`, and writes reaching a caller's object through another
|
||||
function's parameter, are not tracked by this mutation pass. These channels
|
||||
can leave an initializer-derived literal in the graph even after the runtime
|
||||
value changes. Treat such a result as an uncovered mutation channel, not as
|
||||
proof of the current runtime value; a follow-up needs to model that channel
|
||||
and verify its caller/alias behavior.
|
||||
|
||||
Function fingerprints commit normalized syntax, control-flow shape, relation
|
||||
shape, literal sets, arity, and detected effects without using local names or
|
||||
source offsets. Equal duplicate fingerprints remain ambiguous. Dynamic
|
||||
@@ -128,20 +141,64 @@ Nested callable returns are not assigned to their parent callable. Projected
|
||||
graph observations carry source ranges but never source text.
|
||||
|
||||
Return variants pair only when a literal discriminant such as `/type` has one
|
||||
unique occurrence on each side and pairing is reciprocal. Changes use JSON
|
||||
Pointer paths with `added`, `removed`, `changed`, or `unknown` status. A missing
|
||||
field is added or removed only when the relevant parent-property coverage is
|
||||
complete on both shapes. The output includes exact selector candidates,
|
||||
omissions, Evidence links, coverage, and limitations; it does not execute
|
||||
JavaScript. When runtime semantics matter, run behavioral probes directly
|
||||
against the relevant application versions and capture them through the
|
||||
available browser, Electron, or process workflows.
|
||||
unique occurrence on each side and pairing is reciprocal. Source order never
|
||||
implies correspondence. Each retained variant contributes a property inventory
|
||||
of observed JSON Pointer names, including fields whose static values stay
|
||||
unknown, plus that variant's parent-property coverage.
|
||||
|
||||
Changes use JSON Pointer paths with `added`, `removed`, `changed`, or `unknown`
|
||||
status and a separate `presence` object (`present`, `absent`, or
|
||||
`unknown-coverage`) on each side. A name is `added` or `removed` when one paired
|
||||
shape proves presence and the other proves absence. Complete parent-property
|
||||
coverage can establish absence even when field values stay unresolved. Identical literals are
|
||||
omitted. Unresolved values that remain on both sides stay `unknown` when the
|
||||
projections differ, and are omitted when presence is unchanged and the unknown
|
||||
projections match. Incomplete spreads and other partial parent coverage keep
|
||||
one-sided names `unknown` with `unknown-coverage` on the incomplete side.
|
||||
Unpaired variants remain visible as unknown changes and still list their
|
||||
property inventories, each with its own `source_range`. Inventories exclude
|
||||
array holes and slots whose presence was invalidated by mutation; an unresolved
|
||||
value alone does not make an observed property uncertain. `summary.added` and
|
||||
`summary.removed` count
|
||||
presence-level add/remove as well as literal value add/remove;
|
||||
`summary.unknown` does not absorb complete-coverage presence-only gaps.
|
||||
|
||||
The output includes exact selector candidates, omissions, Evidence links,
|
||||
coverage, and limitations; it does not execute JavaScript. When runtime
|
||||
semantics matter, run behavioral probes directly against the relevant
|
||||
application versions and capture them through the available browser, Electron,
|
||||
or process workflows.
|
||||
|
||||
## CLI and verification
|
||||
|
||||
All five CLI commands accept inline JSON or a path to a JSON file. Put the full
|
||||
Evidence records in the workflow input, as in the corresponding MCP tool; the
|
||||
CLI does not resolve Evidence IDs from a separate bundle.
|
||||
All five CLI commands accept inline JSON or a path to a JSON file. The CLI
|
||||
returns an Evidence record directly. Put the full records in a later CLI input;
|
||||
a separate CLI process has no retained MCP connection state.
|
||||
|
||||
For a literal string trace, analyze your supplied tree once, then build the
|
||||
input from the saved Evidence (replace the target and seed):
|
||||
|
||||
```bash
|
||||
rea analyze-javascript-application /absolute/path/to/app --json > application-evidence.json
|
||||
node --input-type=module -e '
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
const application = JSON.parse(readFileSync("application-evidence.json", "utf8"));
|
||||
writeFileSync("trace-input.json", JSON.stringify({
|
||||
application, seed: { kind: "string", value: "search-result" }, direction: "both"
|
||||
}));'
|
||||
rea trace-application-feature ./trace-input.json --json
|
||||
```
|
||||
|
||||
MCP analysis returns the complete Evidence record, with the operation result
|
||||
in `normalized_result` and its identity in `evidence_id`. If the connected server advertises retained references, reuse its
|
||||
exact returned ID as `{"kind":"retained-evidence","evidence_id":"RETURNED_ID"}`
|
||||
in `application`, or `left`/`right` for comparisons. `RETURNED_ID` is a template,
|
||||
not a literal valid ID. Native Evidence arrays still use complete records.
|
||||
References are scoped to one connection; `close_binary` clears them. Export a
|
||||
bundle before closing and import it on another connection, or supply the full
|
||||
inline Evidence there. Versions before 4.1.0 accept full inline Evidence only; installing
|
||||
newer skill instructions does not change that schema. See
|
||||
[MCP Evidence inputs](mcp-contracts.md#retained-application-evidence-inputs).
|
||||
|
||||
For two operator-provided directories or ASARs, run:
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ command and the generic route return their complete results inline. Native
|
||||
targets, single JavaScript files, `.app` bundles, and explicit deep-provider or
|
||||
snapshot requests retain the native deep-analysis route.
|
||||
|
||||
Both CLI routes emit `rea_progress` JSON lines on stderr as inventory, source
|
||||
parsing, graph construction, validation, and Evidence hashing begin. Stdout
|
||||
remains the selected result document. Completion is reported after Evidence
|
||||
creation; a failed analysis retains its typed diagnostic result.
|
||||
|
||||
Configure an MCP client with the ordinary REA setup command:
|
||||
|
||||
```bash
|
||||
@@ -52,6 +57,67 @@ manifest and graph commitments, JavaScript Application Graph, static
|
||||
Electron summary, reconstruction statistics, and explicit limitations. It does
|
||||
not require a live Hopper, Ghidra, browser, or Electron process.
|
||||
|
||||
## ASAR integrity
|
||||
|
||||
ASAR inventory checks Electron integrity metadata for embedded archive entries
|
||||
and supplied `.asar.unpacked` companion files. An integrity failure identifies
|
||||
the logical path, declared and calculated SHA-256 values, and whether the entry
|
||||
was unpacked. By default, a mismatch is returned as a failure with its artifact
|
||||
context. Requests that support `integrity_policy` can explicitly select
|
||||
`record-and-continue` to inspect verified siblings while retaining the mismatch.
|
||||
|
||||
An unpacked entry whose companion bytes were not supplied remains
|
||||
`unavailable`. REA continues analyzing embedded JavaScript and records the
|
||||
missing native/resource bytes as unknown. See [what is reconstructed](#what-is-reconstructed)
|
||||
for the inventory fields and [MCP integrity handling](mcp-contracts.md#integrity-record-and-continue)
|
||||
for the tool-result contract.
|
||||
|
||||
## Large results
|
||||
|
||||
Application analysis projects semantic relationships one source file at a time.
|
||||
After projection, it retains only the lexical module facts and exported return
|
||||
shapes needed to build application relationships. Full source IR does not stay
|
||||
resident for every file together. Callable ownership and contained-reference
|
||||
queries use per-file range indexes, while call sites use exact-range lookups.
|
||||
Existing node budgets and coverage reporting remain in effect.
|
||||
|
||||
Progress identifies the source file being parsed and projected. Cancellation is
|
||||
checked between files, with event-loop yields that allow notifications and
|
||||
completed file-local allocations to be released. A single file's synchronous
|
||||
parser and semantic extraction still need to finish before that boundary.
|
||||
|
||||
Graph and Evidence identifiers hash canonical JSON incrementally, without
|
||||
assembling a single string for the whole graph. The canonical bytes and existing
|
||||
identifiers remain unchanged. The opt-in regression check is
|
||||
`npm run verify:javascript:digests`;
|
||||
it hashes a value larger than the running Node engine's single-string limit.
|
||||
|
||||
Both application CLI routes stream complete JSON and JSONL output in bounded
|
||||
chunks, including field filters and `--full-output`. They wait for each stdout
|
||||
write instead of constructing or reparsing a document-sized string. The opt-in
|
||||
`npm run verify:javascript:output` checks the actual CLI formatting boundary with
|
||||
a temporary output file larger than the running engine's string limit, verifies
|
||||
its bytes and digest, and removes it. Use `-- jsonl` for the compact JSONL check.
|
||||
Each check needs space for one output file plus a 1 GiB free-space reserve.
|
||||
|
||||
Other CLI formats and `--token-count` still assemble whole strings.
|
||||
Field selection remains useful when the caller needs a
|
||||
smaller view, for example `--format json --filter-output
|
||||
evidence_id,normalized_result.statistics`. Streaming output does not bound the
|
||||
memory needed to construct the analysis graph itself.
|
||||
MCP prepares the complete repeated response incrementally against the pinned
|
||||
SDK's 10 MiB stdio receive-buffer budget. Oversized results return an actionable
|
||||
transport constraint and the exact same-session Evidence reference. Use
|
||||
`inspect_analysis_view` with the retained Evidence ID for a summary, one
|
||||
module, or a stable page of module identities. Use `trace_application_feature`
|
||||
once a module, route, or string seed is known, or `export_evidence_bundle` to
|
||||
write the complete canonical bundle without a document-sized allocation.
|
||||
Same-session analysis reads reuse authenticated immutable snapshots; foreign
|
||||
inline Evidence is still parsed and authenticated.
|
||||
See [MCP tool results](mcp-contracts.md#tool-results) for larger client buffers
|
||||
and `REA_MCP_MAX_RESPONSE_BYTES`. Follow-up results remain complete and can also
|
||||
exceed the transport budget.
|
||||
|
||||
## What is reconstructed
|
||||
|
||||
The projector reuses the content-addressed artifact inventory and safe artifact
|
||||
@@ -60,6 +126,13 @@ SHA-256 digests, inventory IDs, ASAR container identity, and `.asar.unpacked`
|
||||
status. Direct ASAR inputs and filesystem-backed ASAR files nested beneath a
|
||||
directory are supported.
|
||||
|
||||
JavaScript sources (`.js`, `.jsx`, `.mjs`, and `.cjs`) and TypeScript sources
|
||||
(`.ts`, `.tsx`, `.mts`, and `.cts`) are parsed as inert text; analysis does not
|
||||
execute them.
|
||||
|
||||
JavaScript and HTML source ranges retain an initial UTF-8 BOM as one UTF-16
|
||||
code unit, matching the original bytes identified by the artifact digest.
|
||||
|
||||
If an ASAR declares an unpacked companion entry but the corresponding
|
||||
`<archive>.unpacked` file is absent from the operator-supplied artifact set, REA
|
||||
keeps the ASAR occurrence with `hash_status: unavailable`, records an explicit
|
||||
@@ -68,6 +141,9 @@ missing bytes. This allows static JavaScript/Electron reconstruction to proceed
|
||||
for the embedded files while preserving the missing native/resource bytes as an
|
||||
unknown instead of silently treating them as absent or verified.
|
||||
|
||||
Plain `.ts` artifact sources use TypeScript syntax without JSX, including
|
||||
angle-bracket type assertions. `.tsx` and `.jsx` retain JSX parsing.
|
||||
|
||||
Selected bounded text is then parsed as inert data to recover:
|
||||
|
||||
- `package.json` metadata and declared main or renderer entrypoints;
|
||||
@@ -91,6 +167,12 @@ Selected bounded text is then parsed as inert data to recover:
|
||||
- utility-process entrypoints and native `.node` binding requests without
|
||||
parsing or executing the add-on.
|
||||
|
||||
Overloaded `.open` calls use lexical receiver facts: ambient browser window and
|
||||
document receivers are treated as browsing-context or document operations,
|
||||
while locally shadowed receivers can still contribute network endpoint
|
||||
candidates. A template recovered with a parser error and no cooked value stays
|
||||
dynamic; its raw spelling is not treated as a valid JavaScript string.
|
||||
|
||||
Each recovered bundle module retains the exact factory-source digest. A complete
|
||||
bounded AST also receives a `babel-ast-v1` structural fingerprint that ignores
|
||||
ordinary identifier names while retaining syntax, literals, operators, object
|
||||
@@ -152,6 +234,22 @@ These safeguards are not caller-selectable output budgets. Source-map parsing
|
||||
reports truncation when its format or parser safety boundary is reached. The
|
||||
application graph has no aggregate node, edge, root, or observation prefix cap.
|
||||
|
||||
Semantic evaluation retains at most 256 distinct primitive candidates per
|
||||
expression and at most 1 MiB of worst-case JSON string bytes across a normalized
|
||||
primitive value's string candidates. The byte budget estimates six JSON bytes
|
||||
per UTF-16 code unit, the expansion bound for escaped strings, and bounds both
|
||||
retained candidate strings and temporary canonical-key serialization. This is
|
||||
an in-memory semantic allocation bound independent of CLI or MCP transport
|
||||
budgets. Source literal bytes remain in the parsed source and artifact evidence;
|
||||
when a normalized semantic value exceeds the bound, its value is unknown and
|
||||
coverage is partial. Expression
|
||||
evaluation and provenance walks stop after 256 nested levels. Union products
|
||||
and string lengths are checked before allocating combinations or concatenated
|
||||
strings. When a boundary is reached, the value stays unknown, semantic coverage
|
||||
becomes partial, and graph evidence retains the expression location and typed
|
||||
limiting reason. The number of unresolved alternatives is unknown; it is not
|
||||
reported as an exact omission count.
|
||||
|
||||
Byte, entry, path, and graph-shape bounds are hard limits. The parse deadline is
|
||||
checked before and between bounded parsing and traversal phases; the synchronous
|
||||
Babel and JSON parser calls cannot be preempted mid-call, so their input byte
|
||||
@@ -176,3 +274,29 @@ paired, ambiguous, and unpaired IPC, validation candidates, utility processes,
|
||||
and native binding requests. These fixtures establish parser and artifact-reader
|
||||
claims; they do not replace the later operator-supplied real-application
|
||||
benchmark.
|
||||
|
||||
URI schemes are classified independently of local file names: a reference such
|
||||
as `web3:app.js` remains external even when an artifact has that literal name.
|
||||
Scheme characters may include digits after the initial letter; a relative path
|
||||
such as `./web3:app.js` still names a local artifact.
|
||||
|
||||
Package `exports` fallback arrays are supported both at the top level and under
|
||||
the root `"."` entry. The resolver selects targets in declared order using the
|
||||
same conditional and invalid-entry handling as nested exports arrays.
|
||||
|
||||
ESM relative module paths and selected package exports targets use URL suffix handling
|
||||
and one percent-decoding pass. CommonJS relative paths and legacy package main
|
||||
fields retain literal filename punctuation. Exports targets must start with
|
||||
`./` and exclude `.`, `..`, and `node_modules` path segments, including their
|
||||
encoded forms, before URL normalization. Arrays skip invalid exports targets
|
||||
in declared order; URL decoding and file lookup failures for a selected target
|
||||
do not select a later entry. Rejected references retain the package metadata
|
||||
path, original target, failed constraint, and importing source location.
|
||||
|
||||
HTML script references resolve to exact inventoried files after applying the
|
||||
document base and query/fragment rules. CommonJS module lookups retain extension
|
||||
and directory resolution.
|
||||
Unresolved HTML references retain their declaration, source range, and resolution
|
||||
reason in the renderer observations.
|
||||
HTML script source ranges follow the HTML parser across LF, CRLF, and bare CR
|
||||
line endings, preserving UTF-16 columns.
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# JavaScript source recovery
|
||||
|
||||
`recover_javascript_sources` / `recover-javascript-sources` derives readable
|
||||
modules from one explicitly selected UTF-8 JavaScript file or bundle. Use it on
|
||||
files returned by `export_web_scripts` or other local scripts. The operation
|
||||
never executes the selected or recovered application code.
|
||||
|
||||
## Caller-supplied engine
|
||||
|
||||
The initial adapter is verified on Linux x64 with Wakaru **1.13.0**. Set
|
||||
`REA_WAKARU_COMMAND` to the absolute path of an executable supplied by the caller.
|
||||
The adapter uses util-linux `prlimit` (default `/usr/bin/prlimit`; override with
|
||||
an absolute `REA_JAVASCRIPT_PRLIMIT_COMMAND`). REA does not install either tool.
|
||||
Engine configuration is checked only when recovery is requested; other REA
|
||||
capabilities remain available without it.
|
||||
|
||||
Unmodified upstream source is retained in `third_party/wakaru`, pinned to
|
||||
`6070266d24b32951fa2fe1d2dad5b4313bd5c81b` (v1.13.0, Apache-2.0).
|
||||
The result reports the executed binary's exact path, SHA-256 and observed version.
|
||||
The audited source revision is distinct from `executed_source_revision: null`:
|
||||
a version string cannot prove which source produced a caller-supplied binary.
|
||||
|
||||
## Recover and analyze
|
||||
|
||||
```sh
|
||||
rea recover-javascript-sources /tmp/web-scripts/app.js /tmp/recovered-app --json
|
||||
rea analyze-javascript-application /tmp/recovered-app/modules --json
|
||||
```
|
||||
|
||||
MCP input:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/tmp/web-scripts/app.js",
|
||||
"output_directory": "/tmp/recovered-app"
|
||||
}
|
||||
```
|
||||
|
||||
The output directory must be absent, absolute, and have an existing parent.
|
||||
The source is snapshotted in a private workspace, and the engine writes only to
|
||||
its own staging directory. Reported module paths, input identities, UTF-8 byte
|
||||
ranges, regular files and emitted map representations are validated before
|
||||
publication. REA writes verified bytes to an exclusively owned output tree and
|
||||
rolls it back on failure. It also checks that the original source and engine
|
||||
bytes remain unchanged before committing. The existing output is never overwritten.
|
||||
|
||||
Published contents:
|
||||
|
||||
- `inputs/bundle.js`: exact original snapshot; its digest binds the Evidence.
|
||||
- `modules/`: derived modules and any upstream-emitted source maps.
|
||||
- `reports/stdout.json` and `reports/provenance.json`: exact producer reports.
|
||||
- `manifest.json`: identities, modules, transform choices, warnings and limitations.
|
||||
|
||||
The inline result includes complete file paths/digests/sizes, extraction ranges,
|
||||
warnings and `analysis_input`, directly usable as the arguments to
|
||||
`analyze_javascript_application`. Raw Evidence retains selected command arguments,
|
||||
reported input paths, exit status, stderr and reports; temporary paths are recorded
|
||||
as historical execution coordinates after their workspace is removed.
|
||||
|
||||
## Transform choices and interpretation
|
||||
|
||||
`extraction_mode` defaults to `structural` (Wakaru `--unpack=strict`). `heuristic`
|
||||
uses `--unpack=auto`; `inspection` uses `--unpack=inspect` and can return finer,
|
||||
non-executable regions. `rewrite_level` accepts `minimal`, `standard` (default)
|
||||
and `aggressive`. Maps and provenance are requested in every operation.
|
||||
|
||||
Structural extraction can fall back to a single file. In the real fixtures,
|
||||
webpack 5 yields two modules; the esbuild scope-hoisted fixture remains one
|
||||
unknown-format script. Returned module counts and detected formats report those
|
||||
actual outcomes. A valid report with failed transformations returns `partial`
|
||||
with original warnings and files. An unusable report, inconsistent provenance,
|
||||
escaping path, symlink or missing module is an error, with owned cleanup.
|
||||
|
||||
Evidence confidence is `derived`. Extraction ranges are half-open **UTF-8 byte
|
||||
offsets in the original snapshot**, rather than rewritten line positions or a
|
||||
coverage assertion. Source maps are preserved without claiming independently
|
||||
verified mapping accuracy. Original variable names and arbitrary runtime
|
||||
equivalence remain unknown. No network fetching occurs.
|
||||
|
||||
## Resource policy and verification
|
||||
|
||||
Each operation uses one worker, a 1 GiB address-space ceiling and a 120-second
|
||||
deadline; it accepts up to 64 MiB of input and 128 MiB/10000 entries of staged
|
||||
output, with 8 MiB per diagnostic/report stream. Exceeding a limit is an explicit
|
||||
failure with rollback. These limits apply per operation and do not claim aggregate
|
||||
host containment across concurrent callers.
|
||||
|
||||
See [testing.md](testing.md#javascript-source-recovery) for the focused real
|
||||
Wakaru CLI/MCP lane and its source-owned compiler fixtures. Other host platforms
|
||||
and Bun/Vue recovery are not claimed by this adapter.
|
||||
@@ -18,6 +18,24 @@ rea observe-javascript-runtime http://127.0.0.1:9229 TARGET_ID \
|
||||
--runtime-kind node --json
|
||||
```
|
||||
|
||||
## Start a caller-owned Node target
|
||||
|
||||
Run your selected application with Inspector bound to loopback:
|
||||
|
||||
```bash
|
||||
node --inspect=127.0.0.1:9229 ./app.mjs
|
||||
```
|
||||
|
||||
Keep it running while listing targets and observing the selected ID. Use a free
|
||||
port and the same literal endpoint in both REA calls. Stop the application
|
||||
through its normal lifecycle when finished; REA disconnects but does not own or
|
||||
terminate it. Node.js must already be installed.
|
||||
|
||||
Use `--inspect` for this passive workflow. `--inspect-brk` pauses before the
|
||||
application starts, and REA never resumes it or sends
|
||||
`Runtime.runIfWaitingForDebugger`. Resume a paused target with a separate
|
||||
caller-owned debugger before requesting application runtime observations.
|
||||
|
||||
The caller supplies the literal-loopback HTTP endpoint directly. Selecting an endpoint exposes all targets it serves and their local script locations or renderer origins. A target must match the exact requested target ID and expose a same-port WebSocket reported by that endpoint. Targets already marked attached are rejected rather than displacing another debugger. Local paths are canonicalized after symlink resolution.
|
||||
|
||||
`list_javascript_runtime_targets` returns every available target in one inline
|
||||
@@ -101,12 +119,18 @@ targets belong to the same Electron application.
|
||||
|
||||
## Capture and determinism
|
||||
|
||||
Every observation records its time window and returns all valid script and
|
||||
execution-context events received during it. Per-location protocol validation
|
||||
remains in force. Scripts are validated after capture, deduplicated by stable
|
||||
metadata, and canonically sorted. Wall-clock timestamps and protocol script IDs
|
||||
are excluded from the durable result, so identical inputs and captured metadata
|
||||
produce the same Evidence ID.
|
||||
Capture retains valid script and execution-context metadata within an 8 MiB
|
||||
aggregate budget, including conservative estimates for UTF-16 strings, record
|
||||
and collection overhead, and projected URL fields. Each CDP message also has a
|
||||
16 MiB transport limit. A message over that limit fails with `payload_limit`;
|
||||
when the aggregate budget is reached, later metadata events are dropped and
|
||||
the partial result reports `events_dropped`, `truncated: true`, and
|
||||
`retained_metadata_budget_exceeded`. Repeated context transitions update the
|
||||
current context record instead of accumulating duplicate objects. Scripts are
|
||||
validated after capture, deduplicated by stable metadata, and canonically
|
||||
sorted. Wall-clock timestamps and protocol script IDs are excluded from the
|
||||
durable result, so identical inputs and captured metadata produce the same
|
||||
Evidence ID.
|
||||
|
||||
Inspector attach is inherently incomplete. Enabling `Debugger` reports known
|
||||
and uncollected scripts, but scripts collected before attachment may be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Managed-code analysis plan
|
||||
# Managed-code analysis and planned extensions
|
||||
|
||||
REA inspects .NET PE/CLI artifacts without loading or executing their code.
|
||||
It can identify an assembly, inspect metadata and CIL, compare members across
|
||||
@@ -25,7 +25,21 @@ part of the current tool set.
|
||||
|
||||
This guide describes the implementation and verification of
|
||||
[ADR-0003](adr/0003-managed-code-evidence-and-provider-boundary.md). The canonical
|
||||
tool inventory is [`product-catalog.json`](product-catalog.json).
|
||||
tool inventory is the [build-generated catalog](mcp-contracts.md#generated-catalog).
|
||||
|
||||
## Shipped scope
|
||||
|
||||
The canonical parser admits PE/CLI bytes and their metadata/CIL without loading
|
||||
an assembly. It reports observed implementation markers and unavailable facts;
|
||||
it does not unpack .NET single-file hosts, decode IL2CPP metadata, or infer a
|
||||
NativeAOT identity from an ordinary native PE. Inputs without admitted CLI
|
||||
metadata do not become managed assemblies through naming or routing guesses.
|
||||
Inspect separately obtained components explicitly and preserve their identities.
|
||||
|
||||
The broader deployment classification and native-body mapping below are design
|
||||
goals from ADR-0003, not claims that every row has an implemented parser. A
|
||||
valid marker can establish a candidate native boundary, not recovered native
|
||||
semantics or runtime behavior.
|
||||
|
||||
## Analysis objective
|
||||
|
||||
@@ -43,9 +57,9 @@ collapsing them:
|
||||
The ordinary workflow ends at question four. Static analysis never loads or
|
||||
executes the target.
|
||||
|
||||
## Classification workflow
|
||||
## Planned deployment classification
|
||||
|
||||
Classification proceeds from the outermost authenticated bytes inward:
|
||||
The intended extended classifier proceeds from outermost authenticated bytes inward:
|
||||
|
||||
```text
|
||||
source path
|
||||
@@ -58,7 +72,7 @@ source path
|
||||
-> managed-only, native-only, composed, degraded, or unsupported route
|
||||
```
|
||||
|
||||
The result is a vector rather than one label. For example, a single-file modern
|
||||
The planned result is a vector rather than one label. For example, a single-file modern
|
||||
.NET deployment can contain a native host, ordinary CIL assemblies, and
|
||||
ReadyToRun components. Each component receives its own digest, classification,
|
||||
coverage, and route while retaining the outer bundle commitment.
|
||||
@@ -76,8 +90,59 @@ coverage, and route while retaining the outer bundle commitment.
|
||||
| Obfuscated assembly | Same positive byte observations as its underlying deployment form | Meaning inferred only from names |
|
||||
| Malformed/unsupported | Exact admitted regions and failure locations | Completeness, successfully skipped rows, or semantics beyond the admitted parser boundary |
|
||||
|
||||
### NativeAOT metadata recovery
|
||||
|
||||
NativeAOT removes the ordinary CIL method bodies, so a CIL decompiler cannot
|
||||
reconstruct those bodies as C# source. The executable remains a native target
|
||||
for Hopper or Ghidra, where decompilation produces pseudocode and analyst
|
||||
inference. This is a different route from ordinary managed member inspection.
|
||||
|
||||
NativeAOT is not limited to Windows `.exe` files. The native image can be PE,
|
||||
ELF, or Mach-O, and NativeAOT can also publish shared libraries (PE DLL, ELF
|
||||
`.so`, or Mach-O `.dylib`) with explicitly exported entry points. Identify the
|
||||
image from its bytes and loader metadata, not its filename extension. When the
|
||||
image is inside an application bundle, inventory or extract the bundle first
|
||||
and carry the component identity into native analysis. Matching PDB, ELF debug,
|
||||
or dSYM sidecars can add symbol evidence when the selected provider supports
|
||||
them; verify their identity and keep them separate from observations made from
|
||||
the executable itself. [Microsoft's NativeAOT overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)
|
||||
lists supported OS/architecture targets and deployment limitations, while its
|
||||
[native library guide](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/libraries)
|
||||
describes exported shared-library entry points.
|
||||
|
||||
The [Ghidra NativeAOT analyzer](https://github.com/Washi1337/ghidra-nativeaot)
|
||||
is a useful optional companion for recovering ReadyToRun metadata. It
|
||||
rehydrates the `DEHYDRATED_DATA` section and annotates method tables, type
|
||||
relationships, vtable slots, frozen objects, and strings. It does not recover
|
||||
original C# method bodies. It is a separate Ghidra extension and interactive
|
||||
metadata browser; REA does not currently install or invoke it through its
|
||||
headless bridge. Its README describes ReadyToRun header discovery, including
|
||||
symbol-based and heuristic paths, so a missed header or unsupported binary
|
||||
should remain an explicit limitation rather than a failed claim about the
|
||||
binary's contents.
|
||||
|
||||
The upstream analyzer currently gates its own analysis to x86-64. NativeAOT's
|
||||
broader platform matrix does not establish REA coverage: PE/COFF, ELF, Mach-O,
|
||||
shared libraries, target architectures, and runtime metadata versions each need
|
||||
their own provider verification. REA's experimental Windows Ghidra boundary is
|
||||
limited to admitted native x86 and x86-64 PE applications, so it does not currently
|
||||
establish coverage for PE DLLs. Mobile NativeAOT targets should remain
|
||||
experimental until REA has a matching provider and package-level verification.
|
||||
|
||||
For REA's native analysis, preserve the same artifact path and digest used by
|
||||
`inspect_managed_artifact`, and report recovered type data as provider
|
||||
observations. Linking those types to native function addresses requires verified
|
||||
provider evidence; names or vtable similarity alone do not prove that a native
|
||||
function implements a specific managed method.
|
||||
|
||||
## Evidence record shape
|
||||
|
||||
Member comparison, application graph projection, and managed/native verification
|
||||
authenticate supplied inspection Evidence and check that any subject SHA-256
|
||||
matches the normalized artifact SHA-256. A valid Evidence ID alone does not
|
||||
establish that those two identities agree. Evidence without a subject remains
|
||||
usable, with no subject digest available to cross-check.
|
||||
|
||||
Every planned operation returns a provider result and Evidence with four
|
||||
commitment groups:
|
||||
|
||||
@@ -225,7 +290,13 @@ Cross-version matching first prefers exact CIL/signature identity. When that
|
||||
does not match, it pairs an exact declared type, method name, and raw signature
|
||||
before trying structural body shape. The exact-signature key uses names only
|
||||
as part of that full tuple; names alone never select a pair. Duplicate tuples
|
||||
remain ambiguous rather than being paired by token order.
|
||||
remain ambiguous rather than being paired by token order. Fields follow the
|
||||
same exact tiers. The raw signature is exact whether or not REA decoded it, so
|
||||
undecoded signatures pair by that tuple too, but they never enter structural
|
||||
rounds. An unmatched member is therefore reported as `unknown`, not added or
|
||||
removed, when its own signature was not decoded or when the other side has an
|
||||
unpaired member, one-sided or ambiguous, with the same declared type and name
|
||||
whose signature was not decoded.
|
||||
|
||||
For a matched method, an unavailable or partial body makes body-shape facets
|
||||
unknown while preserving observed signature differences. Structural identity
|
||||
@@ -257,17 +328,20 @@ or server-side behavior.
|
||||
## Tool and packaging boundary
|
||||
|
||||
The production parser is REA-owned TypeScript and ships with the existing Node
|
||||
application. It is verified against the ECMA-335 format and independent pinned
|
||||
oracles but has no runtime dependency on them.
|
||||
application. Deterministic conformance uses source-owned byte-built PE/CLI
|
||||
fixtures and expected semantic facts. An optional real ILSpy lane runs only when
|
||||
its executable is explicitly supplied; the default lane does not establish
|
||||
independent pinned-oracle parity. Production inspection needs none of those tools.
|
||||
|
||||
- `System.Reflection.Metadata` is the primary independent metadata/CIL oracle.
|
||||
- `ICSharpCode.Decompiler` and `ilspycmd` are reconstruction and differential
|
||||
oracles. An admitted BYO reconstruction operation records its exact version
|
||||
- A pinned `System.Reflection.Metadata` differential oracle is planned; it is
|
||||
not part of the current verifier.
|
||||
- `ICSharpCode.Decompiler`/`ilspycmd` can supply reconstruction inference.
|
||||
A BYO reconstruction import records the supplied version
|
||||
and remains non-canonical. When `REA_ILSPY_CMD_PATH` points to an absolute
|
||||
runnable `ilspycmd`, `verify:managed` runs a source-owned real ILSpy oracle
|
||||
and imports its C# output as reconstruction inference against exact static
|
||||
member Evidence.
|
||||
- dnlib and Mono.Cecil may increase differential coverage. Their mutation APIs
|
||||
- dnlib and Mono.Cecil are potential future differential oracles. Their mutation APIs
|
||||
are not exposed or included in the production parsing boundary.
|
||||
- The package contains no .NET runtime, SDK, ILSpy installation, proprietary
|
||||
assembly, or compiled conformance fixture.
|
||||
@@ -288,6 +362,11 @@ by the selected Hopper/Ghidra host and format matrix.
|
||||
|
||||
## Source-built conformance corpus
|
||||
|
||||
The current lane uses source-owned byte-built fixtures and malformed-input
|
||||
regressions, with optional operator-supplied applications and ILSpy verification.
|
||||
The table below describes the desired expanded corpus; it is not a report that
|
||||
all compiler-generated forms have been independently verified.
|
||||
|
||||
Fixture sources are intentionally small and behavior-focused. Build outputs
|
||||
are generated outside tracked fixture directories and must not remain after
|
||||
verification or packaging.
|
||||
@@ -437,3 +516,11 @@ The managed-code track advances as reviewable pull requests:
|
||||
|
||||
Each implementation PR updates generated product facts only for behavior it
|
||||
actually ships and states which real-tool checks were performed.
|
||||
|
||||
### Method metadata during build comparison
|
||||
|
||||
Managed member comparisons report a `metadata` dimension when an observed
|
||||
MethodDef flags or implementation flags value differs, including accessibility
|
||||
or synchronization changes. This is separate from the CIL/signature matching
|
||||
tiers: identical instructions do not establish unchanged method metadata. The
|
||||
reported difference is static metadata evidence, not proof of runtime behavior.
|
||||
|
||||
+218
-15
@@ -1,5 +1,15 @@
|
||||
# MCP runtime contracts
|
||||
|
||||
## Generated catalog
|
||||
|
||||
Run `npm run docs:generate` in a source checkout to generate the machine-readable
|
||||
catalog at `docs/public/product-catalog.json`. Documentation deployments serve
|
||||
the same file at [/rea/product-catalog.json](/rea/product-catalog.json). PR CI retains it with the packaged
|
||||
skill and portable conformance projections in the `generated-docs` artifact.
|
||||
These outputs describe the exact source revision being built; they are not
|
||||
checked-in snapshots. For a running server, `binary_session` remains the
|
||||
authoritative source of catalog identity and tool availability.
|
||||
|
||||
## Identity and discovery
|
||||
|
||||
`binary_session` reports the active package, server, SDK, and negotiated
|
||||
@@ -8,15 +18,43 @@ registrations, reports their command vectors as aligned, stale, missing, or
|
||||
invalid, and keeps live-server state `unknown` unless the active connection
|
||||
supplies identity.
|
||||
|
||||
Canonical tool names remain stable, while `tools/list` advertises only the
|
||||
operations callable for the current target, provider, host, and
|
||||
negotiated client capabilities. `binary_session.tool_availability` remains the
|
||||
complete inventory: it explains advertised and hidden operations with stable
|
||||
availability reasons and remediation. Each entry also reports required and
|
||||
optional negotiated client features plus the currently missing features.
|
||||
Opening or closing a target or observing a provider health transition emits
|
||||
`tools/list` returns the complete canonical tool inventory, including tools that
|
||||
are currently unavailable. Opening or closing a target or observing a provider
|
||||
health transition leaves that catalog unchanged and does not emit
|
||||
`notifications/tools/list_changed`.
|
||||
|
||||
Advertised input and output schemas contain no reachable recursive references.
|
||||
Schemas share repeated definitions through schema-local references while
|
||||
preserving complete fields and validation rules. Input properties retain their
|
||||
descriptions and literal examples. The SDK advertises an object root for
|
||||
canonical object unions and retains each branch's requirements and exclusions.
|
||||
Tests validate the schemas after SDK conversion and compare advertised
|
||||
validation with actual calls. Individual model APIs can impose additional
|
||||
nesting limits; complete producer captures can exceed ten structural levels.
|
||||
|
||||
`compare_web_captures` accepts exactly one of two input shapes:
|
||||
|
||||
- Passive: `before` and `after` each contain `inspection`, the complete
|
||||
`normalized_result` from `inspect_web_page`. Each may also contain `webmcp`,
|
||||
the complete `normalized_result` from `discover_webmcp_tools`, or null.
|
||||
- Scenario: `before_scenario` and `after_scenario` contain complete
|
||||
`normalized_result` objects from `capture_browser_scenario`. Optional
|
||||
`normalization` defaults to `{ "rules": [] }`.
|
||||
|
||||
The advertised schema includes complete nested capture fields and rejects
|
||||
incomplete pairs, mixed comparison families, and structurally malformed
|
||||
captures. Domain validation additionally checks relationships such as event
|
||||
sequence references and retained counts; JSON Schema does not express those
|
||||
cross-field invariants.
|
||||
|
||||
Call `binary_session` with `{}` and read `result.tool_availability` to choose a
|
||||
callable operation for the current target, provider, host, and negotiated client
|
||||
capabilities. The default result includes the complete inventory with each
|
||||
tool's availability, reason, and remediation. Each entry also reports required
|
||||
and optional negotiated client features plus the currently missing features.
|
||||
The optional inputs `expected_package_version`, `expected_catalog_digest`, and
|
||||
`expected_server_path` compare the live session with the caller's expectations.
|
||||
|
||||
`binary_session.analysis_provider_candidates` is authoritative for deep-engine
|
||||
discovery. Target-free discovery is sorted by provider ID, reports host
|
||||
availability and `unknown` target support, and does not create an analysis
|
||||
@@ -46,24 +84,189 @@ Provider calls receive the request cancellation signal. Artifact traversal,
|
||||
hashing, version comparisons, Hopper requests, and process capture
|
||||
check the same signal. Cancellation is distinct from timeout. A cleanup failure
|
||||
uses `cleanup_incomplete` and lists only the owned resource kinds that remain.
|
||||
Native call tracing and process capture retain available observations in
|
||||
`details.partial_observation` on failure, including when cleanup succeeds.
|
||||
The observation reports its partial coverage; cleanup details describe host
|
||||
state separately from the execution failure.
|
||||
Derived comparisons and reconstruction verification yield before computation
|
||||
and before publication, so cancellation cannot race with successful Evidence.
|
||||
|
||||
CLI calls work without a progress token and translate SIGINT into the same
|
||||
AbortSignal used by providers. Existing controlled-process cleanup and provider
|
||||
shutdown rules still apply; REA never kills a process it cannot prove it owns.
|
||||
`analyze_javascript_application` also yields between reconstruction phases and
|
||||
during graph/result sealing, cross-graph binding checks, Evidence JSON validation,
|
||||
and canonical hashing. Its final result validation reuses exact graphs whose
|
||||
owned constructors validated and completely sealed them; imported graphs still
|
||||
receive full schema and commitment checks.
|
||||
Cancellation observed before completion returns `cancelled` and prevents the
|
||||
provisional result from entering the session ledger; prior Evidence stays usable.
|
||||
Single-file parsing, graph construction, and validation of imported graphs
|
||||
still run synchronously, so control messages can wait for those
|
||||
phases to release the event loop. A rejected client promise alone does not
|
||||
establish that the server has stopped its work.
|
||||
|
||||
Direct CLI analysis calls work without a progress token and translate SIGINT or
|
||||
SIGTERM into the same AbortSignal used by providers. Signal guards remain active
|
||||
through provider cleanup, including repeated delivery by package runners.
|
||||
Existing controlled-process cleanup and provider shutdown rules still apply;
|
||||
REA never kills a process it cannot prove it owns.
|
||||
|
||||
## Ghidra first-query deadlines and recovery
|
||||
|
||||
A successful MCP initialize handshake establishes the REA connection.
|
||||
`open_binary` then selects a target and provider binding; it does not establish
|
||||
that Ghidra has finished importing the target. The first Ghidra-backed query,
|
||||
such as `binary_overview`, starts the engine and waits for import, default
|
||||
auto-analysis, bridge connection, and health readiness before returning analysis.
|
||||
|
||||
These deadlines have different owners:
|
||||
|
||||
| Deadline | Owner and effect |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| MCP initialize | The client bounds transport/REA connection startup, before any target query. |
|
||||
| Ghidra startup | REA allows 330,000 ms by default; `REA_GHIDRA_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid supplied values fail configuration validation. Startup failure is reported by the first provider query. |
|
||||
| Individual tool call | The client bounds its wait, including cold engine startup. The pinned client SDK 2.3.1 defaults to 60,000 ms and can cancel earlier than REA's startup deadline. |
|
||||
|
||||
For an already connected client using the pinned SDK, request options are the
|
||||
**second** argument of `callTool`:
|
||||
|
||||
```js
|
||||
const overview = await client.callTool(
|
||||
{ name: "binary_overview", arguments: {} },
|
||||
{
|
||||
timeout: 240000,
|
||||
onprogress: ({ progress, total, message }) => {
|
||||
console.error({ progress, total, message });
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
The SDK's `onprogress` option supplies a progress token and handles matching
|
||||
notifications. A raw MCP client can instead send `_meta.progressToken` in the
|
||||
request and handle `notifications/progress`. Progress reports do not establish
|
||||
engine readiness or a percentage of Ghidra auto-analysis. A longer server
|
||||
startup allowance does not extend the client's deadline; progress also does not
|
||||
extend it unless the client explicitly implements that policy.
|
||||
|
||||
The 240-second setting is a measured example, not a universal timeout: a
|
||||
controlled Linux x64/WSL2 fixture took about 64 seconds for its first overview
|
||||
with Ghidra 12.1.4 on a mounted NTFS installation, native JDK 21, two CPUs, and a
|
||||
512 MiB Java heap. Target size, storage, and analysis work can change that time.
|
||||
Configure the deadline in the caller's existing request settings; it is not a
|
||||
tool argument, setup grant, or server-side automatic extension. CLI analysis has
|
||||
no MCP client request deadline, but keeps the provider startup deadline and
|
||||
SIGINT cancellation.
|
||||
|
||||
If the client timed out or cancelled during startup, the pending query may have
|
||||
been interrupted. Reissuing `open_binary` for the same active target can reuse
|
||||
the current client, so it is not a fresh-start recovery. On the same connection:
|
||||
|
||||
1. Keep any useful inline Evidence, or export a bundle before closing if retained
|
||||
records are needed. `binary_session` can show the selected binding and its
|
||||
recorded state; target selection alone does not prove engine readiness.
|
||||
2. Call `close_binary` and check its result. It drains owned work, closes the
|
||||
provider, and clears retained session records. A `cleanup_incomplete` result
|
||||
must be addressed according to its reported owned resources before retrying.
|
||||
3. Call `open_binary` with the same caller-selected path and
|
||||
`provider_id: "ghidra"`, then retry the first query with an appropriate client
|
||||
deadline. The connection and selected provider do not need to change.
|
||||
|
||||
This close/reopen flow was exercised on one real Linux stdio connection after a
|
||||
controlled startup timeout, followed by a successful overview and function
|
||||
analysis. It is not a Windows/macOS coverage claim. REA cleans only resources it
|
||||
owns and never switches to another provider automatically. A provider timeout,
|
||||
installation failure, or host permission denial needs its own reported recovery;
|
||||
increasing a client deadline alone does not fix those failures.
|
||||
|
||||
## Tool results
|
||||
|
||||
Evidence-producing tools return `{ result, evidence_id, evidence }` in both
|
||||
text and structured content. `evidence` is the complete canonical Evidence
|
||||
record, including `normalized_result`, which equals `result`. The same record
|
||||
is retained in the session bundle. Read `result` directly, or pass `evidence`
|
||||
to a compatible comparison tool: `analyze_function` Evidence can be passed
|
||||
Evidence-producing tools return the complete canonical Evidence record in both
|
||||
text and structured content. Read `structuredContent.normalized_result` for the
|
||||
operation result and `structuredContent.evidence_id` for its identity. The same
|
||||
record is retained in the session bundle. `analysis_profile` is always present:
|
||||
a concrete profile object or `null`; either value participates in semantic
|
||||
identity. Records omitting this field are rejected. Pass the returned Evidence
|
||||
directly to a compatible comparison tool: `analyze_function` Evidence can be passed
|
||||
directly to `compare_functions`, and `inspect_artifact` Evidence to
|
||||
`compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
|
||||
retained session history or an explicit bundle for transfer.
|
||||
|
||||
REA prepares complete MCP results within the pinned stdio client's 10 MiB
|
||||
receive-buffer budget, including both text and structured representations and
|
||||
room for the JSON-RPC envelope. Response budget settings are captured at startup;
|
||||
restart or recreate the server to apply changes. If a result cannot fit, REA returns
|
||||
`resource_constraint` with `details.resource: "transport"` before constructing
|
||||
a document-sized string. Analysis Evidence remains complete in the current
|
||||
session. Its exact reference is reported in
|
||||
`details.reported_limits.evidence_reference`; call `inspect_analysis_view` with
|
||||
that reference for a summary, one section/module, or a stable page, or call
|
||||
`export_evidence_bundle` with a destination path. Complete
|
||||
bundle exports stream canonical JSON into an atomically published file. A broad
|
||||
follow-up or `get_evidence_bundle` can also exceed the response budget; exporting
|
||||
preserves the complete session without sending it through a single MCP frame.
|
||||
Cancelling an export stops further serialization and removes its staging file.
|
||||
The destination changes only when a complete export is atomically published.
|
||||
|
||||
Clients that explicitly configure a larger receive buffer can set the REA
|
||||
server's `REA_MCP_MAX_RESPONSE_BYTES` environment variable to the same byte
|
||||
count. This setting must be a safe decimal integer at least 10485760; REA
|
||||
reserves 1024 bytes for the envelope. Raising it restores complete inline
|
||||
delivery for responses that fit that buffer and Node's single-string limit.
|
||||
It does not change the client's buffer, analysis coverage, or retained content.
|
||||
Ordinary responses keep their existing complete result contract.
|
||||
|
||||
## Retained application Evidence inputs
|
||||
|
||||
`inspect_analysis_view` projects a caller-selected view of already completed
|
||||
`inspect_binary_layout` or `analyze_javascript_application` Evidence. Source is
|
||||
an exact same-session retained reference or portable inline Evidence. Views are
|
||||
a summary, a layout mitigations or linkage facet, one section/symbol/module, or
|
||||
a stable page with a caller-selected positive `limit`. Module pages include
|
||||
JavaScript assets, bundled modules, and source modules. Select an exact
|
||||
`node_id` when a path is ambiguous or unavailable. Module items retain their
|
||||
recorded property values and source locations; summaries include parent
|
||||
application and semantic coverage. The result carries a digest of the selected
|
||||
facts. Actual serialized size determines MCP transport admission: reduce the
|
||||
page size or export retained Evidence if the view is too large.
|
||||
|
||||
`trace_application_feature`, `trace_javascript_semantics`,
|
||||
`compare_application_versions`, `compare_source_to_bundle`, and
|
||||
`compare_javascript_export_shapes` accept complete inline application Evidence
|
||||
or an exact reference to a record already retained by the current connection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "trace_application_feature",
|
||||
"arguments": {
|
||||
"application": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<64 lowercase hex characters>"
|
||||
},
|
||||
"seed": { "kind": "module", "value": "search.js", "match": "exact" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the `evidence_id` returned by `analyze_javascript_application` (or another
|
||||
compatible application-graph producer). Comparisons accept this form in `left`
|
||||
and `right`; each side can independently be inline or retained. Native
|
||||
observation arrays continue to take complete inline Evidence. Results and their
|
||||
Evidence remain complete inline, and both input forms pass the same semantic,
|
||||
identity, authority, and provenance checks without running the producer again.
|
||||
|
||||
References belong to the current connection's Evidence ledger. Opening another
|
||||
target preserves retained records; `close_binary` clears them, even when no
|
||||
binary is active. A fresh connection has its own ledger. A missing reference
|
||||
reports its exact ID and `details.reason: "missing"`; the server cannot infer
|
||||
whether it was never recorded, cleared, or retained by another connection.
|
||||
Supply complete inline Evidence, repeat its producer, or import an exported
|
||||
Evidence bundle before referencing that imported record. Export a bundle before
|
||||
closing if the investigation needs it later.
|
||||
|
||||
CLI application workflows continue to read portable inline Evidence from files
|
||||
and run the same analysis workflows; a standalone CLI invocation cannot resolve
|
||||
another MCP connection's retained records. No additional lookup call, provider
|
||||
selection, or approval step is required for a same-session follow-up.
|
||||
|
||||
## Aggregate native context
|
||||
|
||||
`get_navigation_context` composes the selected document, current address, and
|
||||
|
||||
@@ -25,6 +25,15 @@ Tool results include their Evidence inline. Prompts do not require a bundle or
|
||||
resource fetch to read a result; an Evidence ID is only a reference when a
|
||||
prompt argument or tool explicitly accepts one.
|
||||
|
||||
For a JavaScript/Electron application directory, `investigate_feature` points
|
||||
directly to `analyze_javascript_application` with `input_path`; it does not
|
||||
require `open_binary`. When that Evidence, or `inspect_binary_layout` Evidence,
|
||||
is already retained, it suggests `inspect_analysis_view` before tracing a
|
||||
feature seed. The CLI equivalent is `rea analyze <directory>`.
|
||||
`open_binary` and the doctor target check admit files and macOS app bundles.
|
||||
A directory outside that opening route is reported with an available analysis
|
||||
action; this does not establish support for every directory format.
|
||||
|
||||
For target paths in `compare_application_versions`, the prompt suggests
|
||||
opening each target before calling `inspect_artifact`; inspection operates on
|
||||
the active target and returns the graph and findings together inline.
|
||||
|
||||
+278
-14
@@ -6,6 +6,40 @@ coverage and unknowns. The CLI and MCP use the same application workflows.
|
||||
|
||||
## Static inspection
|
||||
|
||||
`inspect_macho` retains otool segment `file_offset` values relative to the
|
||||
selected Mach-O slice. Its evidence file-offset ranges address the original
|
||||
input file and include the observed lipo slice offset for universal binaries.
|
||||
If that offset is unavailable, segment evidence locations are omitted with an
|
||||
explicit limitation; architecture inventory locations remain available.
|
||||
|
||||
Hopper's `address_to_file_offset` combines the native image-relative mapping with
|
||||
original-file coordinates only after checking the source against the session's
|
||||
SHA-256. FAT tables and embedded Mach-O headers come from that same hashing pass.
|
||||
A changed or nonregular source reports `artifact_changed`; an unreadable or
|
||||
missing source reports its access or filesystem failure. Those errors retain the native offset,
|
||||
loaded header and provider source path in `details.partial_observation`, with the
|
||||
original-file coordinate explicitly unavailable. Restore the original bytes or
|
||||
reopen the changed executable to obtain verified coordinates. Snapshot replay
|
||||
remains historical evidence for its recorded artifact identity.
|
||||
|
||||
A selected Hopper database's digest identifies the database rather than its
|
||||
original executable. Original-file mapping is unavailable for that selection;
|
||||
native mapping facts remain in partial evidence, and `read_bytes` still reads
|
||||
the loaded database. Open the original executable to verify source coordinates.
|
||||
|
||||
Native Xcode command execution uses shared process supervision with a 60-second
|
||||
deadline and a 64 MiB aggregate stdout/stderr budget. Timeout, cancellation,
|
||||
stream failure, and output exhaustion retain the captured output, exit details,
|
||||
and cleanup outcome in the error result. Truncated output is never parsed as a
|
||||
complete observation.
|
||||
|
||||
- `trace_dylib_resolution` / `rea trace-dylib-resolution <app-or-mach-o>`
|
||||
parses Mach-O load commands in TypeScript and follows dyld's path expansion
|
||||
for every executable in an app bundle, or for one Mach-O within its directory.
|
||||
It reports each `@rpath`, `@loader_path` and `@executable_path` candidate with
|
||||
its outcome. Paths outside the analyzed root, including shared-cache system
|
||||
libraries, stay undetermined. See
|
||||
[Apple application analysis](apple-application-analysis.md#dylib-load-resolution).
|
||||
- `inspect_asset_catalog` / `rea inspect-asset-catalog <app>` reads compiled
|
||||
`Assets.car` metadata through macOS `assetutil --info`. Catalog digests, raw
|
||||
rendition fields, pagination and exact UI resource-name matches are returned.
|
||||
@@ -24,8 +58,30 @@ coverage and unknowns. The CLI and MCP use the same application workflows.
|
||||
connections and controller/class names. `verify:interface-builder` compiles
|
||||
a source-owned AppKit XIB with `ibtool`. Storyboards require an installed
|
||||
iOS platform; unsupported archive forms remain explicit.
|
||||
- `inspect_native_dispatch_metadata` / `rea inspect-native-dispatch-metadata
|
||||
<app-or-binary>` prefers a validated macOS Mach-O byte reader. It decodes
|
||||
XML plist archives accept UTF-8 and BOM-marked UTF-16 in either byte order.
|
||||
App bundle `Info.plist` executable resolution uses the same decoder as keyed
|
||||
archives and Interface Builder. The decoder consumes an initial
|
||||
BOM and rejects malformed byte sequences, unsupported encoding declarations,
|
||||
and declarations that disagree with the detected encoding. It never substitutes
|
||||
UTF-8 for a declared encoding it cannot process. Archive and evidence digests
|
||||
still identify the original serialized bytes. Bundle resolution reports
|
||||
malformed bytes and unsupported encodings against the original plist path
|
||||
before looking up the executable. Interface Builder decoding
|
||||
limits aggregate input archives to 32 MiB.
|
||||
Before decoding, it also bounds the serialized structure: expanded binary
|
||||
plist references, NIB table records, XML elements and text, plus their
|
||||
projected representations must fit the 256 MiB aggregate decode budget.
|
||||
NIB string values, data, property names and class names are charged at each
|
||||
projected JSON occurrence, including when the archive reuses one source
|
||||
value. Binary plist data projection also accounts for the temporary byte
|
||||
copy made by the pinned decoder projection. These are format-derived
|
||||
representation reservations, not exact heap measurements or a process-wide
|
||||
heap ceiling.
|
||||
Archives beyond either aggregate budget are omitted with their paths and an
|
||||
explicitly partial `archive_decode` facet; previously decoded documents and
|
||||
their identities remain available.
|
||||
- `inspect_native_dispatch_metadata` /
|
||||
`rea inspect-native-dispatch-metadata <app-or-binary>` prefers a validated macOS Mach-O byte reader. It decodes
|
||||
64-bit little-endian Objective-C class/metaclass records, superclass pointers,
|
||||
absolute/relative method entries, ivar offsets/sizes/alignment and protocol
|
||||
declarations. It also decodes simple Swift conformances, static synchronous
|
||||
@@ -33,15 +89,89 @@ coverage and unknowns. The CLI and MCP use the same application workflows.
|
||||
vtable descriptors. Records include exact virtual addresses/file offsets and
|
||||
artifact evidence. Vtable indexes are metadata word offsets; witness indexes
|
||||
start after the conformance header. Names unavailable in metadata remain null.
|
||||
Chained fixups, external bindings, authenticated pointers, categories,
|
||||
properties, generic/resilient/async/coroutine tables and inherited overrides
|
||||
have explicit unsupported or partial coverage. Other providers retain the
|
||||
Pointers are decoded through the image's fixups:
|
||||
- `LC_DYLD_CHAINED_FIXUPS` rebases and binds, including the authenticated
|
||||
arm64e formats;
|
||||
- legacy `LC_DYLD_INFO` bind opcodes.
|
||||
|
||||
So an external superclass resolves from its `_OBJC_CLASS_$_` bind (for
|
||||
example `NSObject`). The `pointer_fixups` coverage facet names the mechanism.
|
||||
Class properties, with parsed attributes, and `__objc_catlist` categories are
|
||||
also decoded. Categories record the extended class, local or external, plus
|
||||
their methods, protocols and properties; category methods appear as
|
||||
implementations with a `category`. Swift field-offset globals that are only
|
||||
initialized at runtime stay unresolved. Generic, resilient, async and
|
||||
coroutine tables, and inherited overrides, have explicit unsupported or
|
||||
partial coverage. Other providers retain the
|
||||
existing symbol-based inventory with its narrower coverage.
|
||||
|
||||
## Exact CLI selectors
|
||||
|
||||
`function`, `instructions` and `decompile` accept `--procedure=<name-or-address>`;
|
||||
`xrefs` accepts `--address=<name-or-address>`. `search` accepts `--pattern=<text>`
|
||||
and `trace` accepts `--query=<text>`. These alternatives preserve names and text
|
||||
that begin with a dash, including Objective-C names such as
|
||||
`rea function ./app '--procedure=-[REAWidget delegate]' --provider hopper --format json`.
|
||||
Use the equals form when the value resembles a CLI flag, for example
|
||||
`rea trace ./app --query=--help --provider hopper --format json`.
|
||||
The existing positional forms remain available. Missing selectors and conflicting
|
||||
positional/named values fail before analysis starts; identical selections agree.
|
||||
|
||||
## Native instruction, call and type primitives
|
||||
|
||||
Ghidra supplies three exact-object operations:
|
||||
|
||||
Ghidra accepts hexadecimal addresses with or without a `0x` prefix, in either
|
||||
letter case, and explicit address-space coordinates such as `EXTERNAL:0x1`.
|
||||
Results use canonical lowercase hexadecimal offsets. For a procedure identifier,
|
||||
an explicit `0x` or address-space prefix selects an address; otherwise an exact
|
||||
database symbol name takes precedence over a bare hexadecimal address. Thus a
|
||||
function renamed to `dead` remains selectable by name. Use the names returned
|
||||
by the inventory, including their namespaces and any platform symbol prefix.
|
||||
Ghidra also accepts secondary symbols at a function entry, such as an imported
|
||||
`_main` label. A label inside a function is not a procedure-name selector; use
|
||||
its address when selecting the containing function.
|
||||
Overloads can share a fully qualified name; ambiguity errors return every
|
||||
matching entry address so the caller can select the intended function directly.
|
||||
|
||||
`resolve_containing_procedure` also resolves an exact external entry. Its empty
|
||||
body remains explicit; nearby external addresses do not inherit that identity.
|
||||
|
||||
Ghidra function references cover the complete function-body AddressSet and its
|
||||
exact entry, including references into instruction interiors and references
|
||||
from embedded data. Addresses inside an enclosing span but outside the owned
|
||||
body are excluded. `procedure_references` returns internal and external edges;
|
||||
`analyze_function` omits incoming edges whose sources belong to the same function.
|
||||
The reference collector's semantics participate in the analysis profile, so
|
||||
older snapshots cannot replay the former incomplete results under this profile.
|
||||
|
||||
Ghidra regex searches use Java Pattern semantics. If compiling or matching a
|
||||
pattern exhausts the engine's stack, REA returns a resource constraint and
|
||||
preserves the active database and annotations. Retry in literal mode or simplify
|
||||
the regex; successful literal searches retain complete matching strings.
|
||||
Cancelling an active request terminates the ephemeral database and discards its
|
||||
annotations. REA retains the selected target and imports the original artifact
|
||||
again on the next Ghidra query.
|
||||
|
||||
Opening the same target and profile again retains the live database, including
|
||||
annotations. It does not make those edits eligible for an immutable snapshot.
|
||||
Snapshot closes drain earlier provider requests before saving and keep later
|
||||
requests waiting until the save and close finish. A concurrent successful edit
|
||||
therefore rejects the snapshot save and leaves the edited session open.
|
||||
After metadata edits, snapshot saves and imports remain unavailable in that
|
||||
session. Use `export_evidence_bundle` to retain the observations, close without
|
||||
`snapshot_path`, and reopen the target before importing or saving a snapshot.
|
||||
|
||||
Ghidra checks its private import copy against the artifact digest selected by
|
||||
`open_binary`. If the source changes before that copy is acquired, the query
|
||||
reports `artifact_changed` with the selected path and both digests. Reopen the
|
||||
stable target to acquire its current identity. An already imported database
|
||||
continues to describe its captured bytes even if the original path is changed
|
||||
or removed; this identity failure does not mark the Ghidra installation unavailable.
|
||||
On Linux and macOS, an unreadable source reports `access_denied`; a removed or
|
||||
nonregular replacement reports `artifact_changed`. Source copying is cancellable
|
||||
and never replaces an existing private snapshot.
|
||||
|
||||
```bash
|
||||
rea inspect-native-instruction <binary> <address> --provider ghidra
|
||||
rea resolve-native-call-targets <binary> <call-site> --provider ghidra
|
||||
@@ -55,10 +185,35 @@ outside-memory and undecodable addresses have separate outcomes. Effective
|
||||
memory base/index/displacement roles and per-instruction context mode remain
|
||||
unavailable; `mode` is the program language variant.
|
||||
|
||||
Ghidra distinguishes a return using decoded instruction p-code's `RETURN`
|
||||
operation. `flow.classification_evidence` preserves the Listing flow type and
|
||||
the observed return evidence; mnemonic spelling alone is not the classifier.
|
||||
Other terminal or trap flows retain the provider's classification, and this
|
||||
inspection does not change function extents or prove a calling convention.
|
||||
The analysis profile commits this decoder behavior. Snapshots from the earlier
|
||||
profile produce a profile mismatch rather than replaying the former generic
|
||||
terminal result. The old snapshot's `evidence_bundle` remains valid retained
|
||||
Evidence: copy that object to a separate JSON file and use `rea evidence-import`
|
||||
to validate it. Run analysis without the old snapshot and save a fresh one
|
||||
under the current profile. Other providers and older captures may omit the
|
||||
classification evidence field.
|
||||
|
||||
Call resolution reports direct, resolved indirect, ambiguous, unresolved and
|
||||
non-call outcomes from static call references. It does not establish runtime
|
||||
execution or classify Objective-C/Swift/vtable/closure mechanisms from names.
|
||||
|
||||
`batch_decompile` keeps each caller-selected symbol or address in `items[].address`.
|
||||
The separate `procedure` field reports the canonical entry and the provider's
|
||||
observed entry label. An interior address therefore remains visible without
|
||||
being mistaken for the function entry. Identity failures are reported as
|
||||
`procedure.status: "unknown"`; label failures leave a null name and `name_error`.
|
||||
Neither failure discards usable pseudocode, and names are not parsed from it.
|
||||
Resolved entries also appear in the composed Evidence locations. New batch
|
||||
Evidence commits the revised workflow profile; retained historical Evidence
|
||||
remains valid. This tool
|
||||
has no dedicated CLI command; the existing `decompile` command remains the
|
||||
single-procedure CLI operation.
|
||||
|
||||
Type inspection selects one exact database pathname or typed data address.
|
||||
Struct/union fields, enums, pointers, arrays, typedefs, size, alignment, packing
|
||||
and bitfields retain database authority. Child types use exact IDs for further
|
||||
@@ -142,23 +297,103 @@ owned helper per scenario, removes its temporary compiler cache and stops its
|
||||
helper on cancellation. It does not launch or own the selected application. UI
|
||||
actions may change application data or trigger network activity.
|
||||
|
||||
## Native call observation
|
||||
|
||||
`observe_native_calls` / `rea observe-native-calls <app-or-mach-o> <input-json>`
|
||||
launches the active Mach-O as a new, owned process under LLDB. It stops at
|
||||
caller-selected entries, records them and continues:
|
||||
|
||||
- functions by exact symbol name, optionally limited to one image;
|
||||
- Objective-C methods by selector, optionally limited to one class and to
|
||||
instance or class methods.
|
||||
|
||||
```sh
|
||||
rea observe-native-calls /Applications/Example.app '{"breakpoints":[{"kind":"objc-method","class_name":"NSURLSession","selector":"dataTaskWithRequest:completionHandler:"},{"kind":"function","name":"open","module":"libsystem_kernel.dylib"}],"duration_ms":5000,"backtrace_frames":4}' --json
|
||||
```
|
||||
|
||||
Each event is an observation: thread, image, symbol, load and file address,
|
||||
the raw integer argument registers, optional caller frames and, for an
|
||||
Objective-C method, the selector read from `_cmd` and the receiver's dynamic
|
||||
class. Breakpoints stop at the symbol itself, so the registers hold the ABI
|
||||
arguments. The receiver class comes from LLDB's Objective-C runtime reader
|
||||
without running target code; no expression is evaluated, and REA never
|
||||
attaches to a process it did not launch.
|
||||
|
||||
The run ends when the process exits, `max_events` entries are recorded,
|
||||
`duration_ms` elapses, or the aggregate trace resource budget is exhausted.
|
||||
Bounded termination reports partial coverage. REA checks process termination
|
||||
using a launch-time OS start identity; if ownership or cleanup cannot be
|
||||
verified, the result or error preserves that uncertainty. The result reports:
|
||||
|
||||
- the outcome, exit status and captured stdout/stderr (1 MiB kept per stream
|
||||
while excess bytes are drained and counted without growing capture files).
|
||||
Each stream reports `complete`; when draining could not finish, its byte
|
||||
count is an observed lower bound and truncation is explicit;
|
||||
- caller-selected arguments, working directory, and environment overrides;
|
||||
- `selected_file_sha256` and LLDB module identity observations. These checks do
|
||||
not pin the executable against concurrent mutation; `loaded_image_sha256`
|
||||
remains unknown;
|
||||
- signal or exception stops;
|
||||
- every breakpoint's resolved locations; a breakpoint that matched no loaded
|
||||
code makes coverage partial;
|
||||
- limitations.
|
||||
|
||||
The bridge limits retained event JSON to 8 MiB and caller frames to 65,536
|
||||
across the run. It checks admission before retaining another complete event;
|
||||
`resource-limit` reports which observations could not be completed without
|
||||
silently changing the requested event or frame settings.
|
||||
Resolved breakpoint locations have a separate aggregate 8 MiB metadata budget.
|
||||
If their details exceed it, `location_count` retains the full observed match
|
||||
count, coverage is partial, and `breakpoint_locations_truncated` is true. This
|
||||
post-run metadata limit does not imply that the target was killed.
|
||||
|
||||
Accepted entries and signal stops are also flushed to a bounded 8 MiB journal.
|
||||
If cancellation, timeout, capture failure, or a later bridge error prevents a
|
||||
full result, the error retains this evidence in `details.partial_observation`
|
||||
with partial coverage. Available output prefixes remain inline; their byte
|
||||
counts are observed lower bounds when final drain counters are unavailable.
|
||||
The original failure classification and cleanup uncertainty remain separate.
|
||||
An interrupted journal row is ignored with an explicit limitation.
|
||||
|
||||
Only entries are observed: return values, floating-point and stack arguments,
|
||||
and inlined or `objc_direct` calls are not. The target runs with the current
|
||||
user's permissions and may change files, show UI or use the network.
|
||||
|
||||
Debugging must be allowed for the user (Developer Tools access). A
|
||||
hardened-runtime target without `com.apple.security.get-task-allow` cannot be
|
||||
debugged; `inspect_signature` reports this as the debugger-attach facet, and
|
||||
the tool fails with a `debugger-attach-denied` reason.
|
||||
|
||||
## Provider and verification boundaries
|
||||
|
||||
Install Ghidra 12.1.4 and a full 64-bit JDK 21 separately, then configure REA to
|
||||
use them. Ghidra analysis supports Linux x64 and macOS x64/arm64; macOS requires
|
||||
the matching native decompiler. Windows Ghidra analysis is unavailable until
|
||||
Job Object process ownership, private runtime DACLs, and reparse-safe path checks
|
||||
are implemented and verified. See [Windows Ghidra P0](windows-ghidra-p0.md) and
|
||||
Install a Ghidra 12.1.x release and the 64-bit full JDK it declares, then configure REA to
|
||||
use them. Ghidra analysis supports Linux x64/arm64 and macOS x64/arm64; each installation requires
|
||||
the matching native decompiler. Experimental Windows x64 P0 admits native
|
||||
x86-64 PE applications on local NTFS using bundled Job Object ownership,
|
||||
protected runtime DACLs, and handle-based path admission. See [Windows Ghidra P0](windows-ghidra-p0.md) and
|
||||
[issue #527](https://github.com/morluto/rea/issues/527).
|
||||
Ghidra has no GUI or mutation authority, and REA never falls back automatically
|
||||
to Hopper.
|
||||
On Linux and macOS, `annotate_native_function` atomically edits a function name
|
||||
and entry comments in the ephemeral database, returning refreshed analysis
|
||||
without changing executable bytes. Annotation text must contain no NUL or
|
||||
unpaired Unicode surrogate; a rejection identifies the field and UTF-16 index
|
||||
and leaves every annotation unchanged. CRLF, supplementary Unicode characters,
|
||||
and combining characters are preserved. Windows P0 remains read-only. Ghidra has no
|
||||
GUI authority, and REA never falls back automatically to Hopper.
|
||||
Ghidra name edits preserve the existing namespace. Supply either a leaf name
|
||||
such as `renamed` or a fully qualified name in that namespace, such as
|
||||
`alpha::renamed`. The returned qualified name can be reused as an idempotent
|
||||
rename input. Edits retain the existing namespace; other namespace-like text
|
||||
remains literal leaf-name text. A qualified name with an empty leaf is rejected
|
||||
before any comment or name is changed.
|
||||
|
||||
- `npm run verify:ghidra`: host-native debug/stripped targets, native type layout,
|
||||
instruction/call facts, value dependencies and process/project cleanup.
|
||||
- `npm run verify:ghidra:aarch64-jump-table`: optimized ELF and byte/halfword
|
||||
relative tables, plus ARM64 Mach-O on an ARM64 macOS host.
|
||||
- `npm run verify:apple-dispatch`: source-built Objective-C protocols/classes and
|
||||
Swift conformances/vtables, repeated after stripping local symbols.
|
||||
- `npm run verify:apple-dispatch`: source-built Objective-C protocols, classes,
|
||||
properties and categories, and Swift conformances and vtables. Each is linked
|
||||
with legacy `LC_DYLD_INFO` and with chained fixups (and as arm64e on Apple
|
||||
silicon), then repeated after stripping local symbols.
|
||||
- `npm run verify:native-ui`: one source-owned fixture window, successful
|
||||
selected-window capture and actions, changed-target rejection, and cleanup.
|
||||
Missing OS permissions fail this lane.
|
||||
@@ -169,3 +404,32 @@ to Hopper.
|
||||
macOS ARM64 is the real host verified during this implementation. Admission of
|
||||
macOS Intel does not claim an Intel verification run. Unsupported metadata and
|
||||
unresolved runtime/value semantics remain visible in results.
|
||||
|
||||
## Interface Builder hierarchy coverage
|
||||
|
||||
A keyed-archive hierarchy UID without an object-table entry marks the hierarchy
|
||||
as partial while preserving decoded objects and known links. UID 0 remains
|
||||
archived nil and does not count as a missing reference. Archive and evidence
|
||||
digests identify the original serialized bytes.
|
||||
|
||||
## Keyed archive integer precision
|
||||
|
||||
The archive reader observes XML number element types and binary number markers.
|
||||
Integers beyond the exact range of a JSON number are reported with the original
|
||||
decimal text as `{ "$plist_type": "integer", "decimal": "<exact digits>" }` when
|
||||
that source value can be associated unambiguously. Integral real values remain
|
||||
numbers. Colliding integer/real values, unclassified numbers, and malformed UID
|
||||
markers remain decoded with an explicit precision limitation. The limitation
|
||||
also counts observed unsafe integer literals, including observations outside the
|
||||
selected object page. Incomplete supplemental metadata produces an explicit
|
||||
precision note without rejecting an archive accepted by the byte decoder.
|
||||
Mixed XML number content (comments, CDATA, processing instructions or child
|
||||
elements) and integer text the supplemental reader cannot classify disable exact
|
||||
association and retain decoded numbers with that precision note. Nonnumeric
|
||||
`UID` and `CF$UID` dictionary values remain ordinary data and are projected
|
||||
recursively; numeric reference markers retain their original representation.
|
||||
|
||||
Value projection preserves serialized node kinds, IDs, references and pagination.
|
||||
Exact and ambiguous value counts describe emitted root/object value instances;
|
||||
a root and an object may contain the same value. Archive digests identify the
|
||||
original bytes.
|
||||
|
||||
+78
-3
@@ -12,14 +12,45 @@ sandbox. Filesystem observation paths select what REA snapshots; they do not
|
||||
restrict what the process can read or write. The inherited environment is not
|
||||
recorded, so its influence may remain unknown.
|
||||
|
||||
## Host support
|
||||
|
||||
Capturing a new scenario currently requires Linux or macOS and a working
|
||||
native PTY backend. Native Windows capture is unavailable because the PTY
|
||||
adapter does not yet verify descendant cleanup. The native Job Object controls
|
||||
used by other REA providers do not establish PTY capture support. Reinstalling
|
||||
the Windows PTY binary does not enable this workflow.
|
||||
|
||||
For Linux commands, use Linux REA inside WSL. Adapt the scenario to that host;
|
||||
this does not establish capture of a native Windows process tree. Comparing
|
||||
existing capture Evidence through the CLI or MCP remains available on Windows
|
||||
and does not launch a PTY or the captured target.
|
||||
|
||||
On supported capture hosts, a missing or incompatible native PTY binary has a
|
||||
different recovery: reinstall REA for the active platform, architecture, and
|
||||
Node.js version with optional dependencies enabled. Capability diagnostics
|
||||
distinguish this from the Windows capture-adapter limitation.
|
||||
|
||||
macOS also requires Apple's Swift compiler through `xcrun` for process
|
||||
ownership inspection. REA compiles its packaged, narrow process-inspection
|
||||
helper into a private temporary directory before a capture or owned provider
|
||||
process starts; the same prerequisite applies to owned provider-process
|
||||
supervision on macOS. REA removes that directory when it exits. REA does not
|
||||
install Xcode, Command Line Tools, or other software. Capability checks prepare
|
||||
this helper before reporting macOS capture as available.
|
||||
|
||||
## Capture a command
|
||||
|
||||
Write a JSON scenario and pass its path to the CLI:
|
||||
|
||||
```sh
|
||||
rea capture-process ./scenario.json > capture.json
|
||||
rea capture-process ./scenario.json --json > capture.json
|
||||
```
|
||||
|
||||
`--json` is required when saving input for JSON consumers; the default terminal
|
||||
format is TOON. The file contains the complete capture Evidence record.
|
||||
Choose an output file distinct from the scenario input: shell redirection opens
|
||||
and truncates the output before REA reads the scenario.
|
||||
|
||||
For example:
|
||||
|
||||
```json
|
||||
@@ -63,6 +94,31 @@ deleted, modified, or unchanged. These are bounded snapshots, not a syscall
|
||||
trace; short-lived changes between snapshots may be missed. With no selected
|
||||
paths, filesystem effects remain unknown.
|
||||
|
||||
A missing entry proves creation or deletion only when the corresponding
|
||||
observation root's path enumeration was exhausted. Otherwise its effect has
|
||||
`status: "unknown"`, the available before/after state, and a reason; a null
|
||||
state in this variant means unobserved. Paths below symlinks that REA did not
|
||||
follow also remain unknown. Partial enumeration of one root does not erase
|
||||
known effects in another, and unavailable content hashes do not by themselves
|
||||
make path absence unknown.
|
||||
|
||||
Terminal frame `data` is the comparison text after the selected normalization.
|
||||
New captures also preserve `raw_data` when normalization changes a PTY chunk;
|
||||
otherwise `data` is already the original text. Older captures may lack the
|
||||
original text for changed chunks. Both values participate in Evidence identity,
|
||||
while comparisons and trace assertions use the normalized text. Retaining the
|
||||
original adds at most `limits.output_bytes` of text, because it comes from the
|
||||
same admitted PTY chunks rather than a second unbounded stream.
|
||||
|
||||
Port normalization recognizes explicit `port`, `tcp_port`, `udp_port`, and
|
||||
`listen` fields, URL authorities, IP endpoints, and `localhost` endpoints, with
|
||||
ports from 0 through 65535. It preserves ordinary counters, dimensions, file
|
||||
line numbers, and ambiguous bare host labels. Use explicit literal `patterns`
|
||||
for a different application-specific spelling. Set `ports: false` to preserve
|
||||
all endpoint numbers in comparison text. Captures commit the port-normalization
|
||||
version so older broad numeric normalization cannot silently compare as the
|
||||
same contract.
|
||||
|
||||
Output, file count, file size, process sampling, filesystem depth, total
|
||||
runtime, idle time, and post-exit settlement are bounded by the scenario's
|
||||
limits. The result marks truncated observations and residual unknowns rather
|
||||
@@ -71,6 +127,20 @@ run the same owned-process cleanup path. Settlement reports whether the
|
||||
sampled process group quiesced or whether cleanup was needed or unverifiable;
|
||||
sampling cannot prove that every short-lived or detached descendant was seen.
|
||||
|
||||
When the host withholds an unrelated process’s ownership token, REA leaves that
|
||||
process untouched and records its PID and reason in `cleanup.unverified_processes`
|
||||
and process residual unknowns. Successful cleanup verifies the owned group;
|
||||
it does not attribute those unrelated processes. Related or otherwise unexplained
|
||||
unreadable processes still prevent successful cleanup.
|
||||
|
||||
On macOS, changing a Node process's `process.title` can make its run token
|
||||
unreadable. Node documents that [setting the title overwrites argv memory](https://nodejs.org/download/release/v24.18.0/docs/api/process.html#processtitle).
|
||||
With the pinned toolchain, a live child retained a readable start identity
|
||||
while its token became unavailable after this change; npm changes its title
|
||||
as well. A newly started unreadable process can prevent verified cleanup even
|
||||
after the selected command exits. REA preserves this uncertainty and leaves
|
||||
that process untouched.
|
||||
|
||||
## Compare two captures
|
||||
|
||||
Compare saved capture Evidence with:
|
||||
@@ -93,7 +163,12 @@ causal evidence. Trace comparison returns `unknown` when a capture lacks the
|
||||
required complete event journal or contains relevant residual unknowns.
|
||||
|
||||
Each capture carries commitments for the selected scenario, executable,
|
||||
comparison contract, and normalization rules. Comparison rejects captures
|
||||
whose comparison contracts differ. Captures are local Evidence files; keep
|
||||
comparison contract, and normalization rules. The comparison contract covers
|
||||
the working directory, explicit environment, filesystem observation paths,
|
||||
terminal size, scripted events, timeouts, limits, and normalization; the
|
||||
executable and its arguments may differ. Comparison rejects captures whose
|
||||
comparison contracts differ and names the differing fields, so run both
|
||||
scenarios from the same absolute working directory and observation paths.
|
||||
Captures are local Evidence files; keep
|
||||
their source artifacts and invocation context available when interpreting a
|
||||
difference.
|
||||
|
||||
@@ -1,554 +0,0 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "rea-agents",
|
||||
"version": "4.0.0",
|
||||
"sdk": {
|
||||
"server": "2.0.0",
|
||||
"client": "2.0.0",
|
||||
"core": "2.0.0"
|
||||
},
|
||||
"skill_version": "24"
|
||||
},
|
||||
"tools": {
|
||||
"total": 116,
|
||||
"families": [
|
||||
{
|
||||
"id": "direct",
|
||||
"surface": "official-proxy",
|
||||
"count": 39,
|
||||
"tools": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"comment",
|
||||
"current_address",
|
||||
"current_document",
|
||||
"current_procedure",
|
||||
"goto_address",
|
||||
"inline_comment",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"list_bookmarks",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"next_address",
|
||||
"prev_address",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"set_address_name",
|
||||
"set_addresses_names",
|
||||
"set_bookmark",
|
||||
"set_comment",
|
||||
"set_current_document",
|
||||
"set_inline_comment",
|
||||
"unset_bookmark",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "enhanced",
|
||||
"surface": "enhanced",
|
||||
"count": 14,
|
||||
"tools": [
|
||||
"analyze_function",
|
||||
"analyze_swift_types",
|
||||
"batch_decompile",
|
||||
"binary_overview",
|
||||
"find_xrefs_to_name",
|
||||
"get_call_graph",
|
||||
"get_objc_classes",
|
||||
"get_objc_protocols",
|
||||
"inspect_native_api",
|
||||
"inspect_native_dispatch_metadata",
|
||||
"trace_call_path",
|
||||
"trace_feature",
|
||||
"trace_native_ui_action",
|
||||
"trace_native_values"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native",
|
||||
"surface": "native-provider",
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_signature",
|
||||
"list_architectures",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "artifact",
|
||||
"surface": "artifact-provider",
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inspect_asset_catalog",
|
||||
"inspect_keyed_archive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "managed",
|
||||
"surface": "managed",
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"inspect_managed_artifact",
|
||||
"inspect_managed_members",
|
||||
"inspect_managed_native_boundaries",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "browser",
|
||||
"surface": "browser-provider",
|
||||
"count": 9,
|
||||
"tools": [
|
||||
"analyze_web_bundle",
|
||||
"capture_browser_scenario",
|
||||
"capture_web_screenshot",
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "electron",
|
||||
"surface": "electron-provider",
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"analyze_javascript_application",
|
||||
"capture_electron_scenario",
|
||||
"inspect_electron_page",
|
||||
"list_electron_targets",
|
||||
"reconcile_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "javascript-runtime",
|
||||
"surface": "runtime-provider",
|
||||
"count": 2,
|
||||
"tools": [
|
||||
"list_javascript_runtime_targets",
|
||||
"observe_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "application",
|
||||
"surface": "application-workflow",
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "session",
|
||||
"surface": "session",
|
||||
"count": 21,
|
||||
"tools": [
|
||||
"binary_session",
|
||||
"build_call_path",
|
||||
"capture_process_scenario",
|
||||
"close_binary",
|
||||
"compare_artifacts",
|
||||
"compare_bundles",
|
||||
"compare_functions",
|
||||
"compare_process_captures",
|
||||
"correlate_static_and_runtime",
|
||||
"export_evidence_bundle",
|
||||
"find_changed_behavior",
|
||||
"get_evidence_bundle",
|
||||
"get_navigation_context",
|
||||
"import_evidence_bundle",
|
||||
"inspect_address_context",
|
||||
"list_unknowns",
|
||||
"open_binary",
|
||||
"record_unknown",
|
||||
"update_unknown",
|
||||
"verify_reconstruction",
|
||||
"verify_unknown_resolution"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"providers": [
|
||||
{
|
||||
"id": "ghidra",
|
||||
"name": "Ghidra",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"address_name",
|
||||
"analyze_function",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "hopper",
|
||||
"name": "Hopper Disassembler",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"analyze_function",
|
||||
"comment",
|
||||
"current_address",
|
||||
"current_document",
|
||||
"current_procedure",
|
||||
"goto_address",
|
||||
"inline_comment",
|
||||
"list_bookmarks",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_segments",
|
||||
"list_strings",
|
||||
"next_address",
|
||||
"prev_address",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"set_address_name",
|
||||
"set_addresses_names",
|
||||
"set_bookmark",
|
||||
"set_comment",
|
||||
"set_current_document",
|
||||
"set_inline_comment",
|
||||
"unset_bookmark",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native-macos",
|
||||
"name": "macOS native inspection utilities",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_signature",
|
||||
"list_architectures",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-artifact-graph",
|
||||
"name": "REA safe artifact graph provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inspect_asset_catalog",
|
||||
"inspect_keyed_archive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-cdp-browser",
|
||||
"name": "REA Chrome DevTools Protocol observation provider",
|
||||
"version": "2",
|
||||
"capabilities": [
|
||||
"analyze_web_bundle",
|
||||
"capture_web_screenshot",
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-cdp-electron",
|
||||
"name": "REA Electron file-page CDP observation provider",
|
||||
"version": "1",
|
||||
"capabilities": ["inspect_electron_page", "list_electron_targets"]
|
||||
},
|
||||
{
|
||||
"id": "rea-dotnet-static",
|
||||
"name": "REA managed static analysis provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"inspect_managed_artifact",
|
||||
"inspect_managed_members",
|
||||
"inspect_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-dotnet-workflows",
|
||||
"name": "REA managed-code workflows",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-application",
|
||||
"name": "REA JavaScript application analyzer",
|
||||
"version": "1",
|
||||
"capabilities": ["analyze_javascript_application"]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-application-workflows",
|
||||
"name": "REA JavaScript application workflows",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-javascript-runtime-reconciliation",
|
||||
"name": "REA JavaScript runtime reconciliation",
|
||||
"version": "1",
|
||||
"capabilities": ["reconcile_javascript_runtime"]
|
||||
},
|
||||
{
|
||||
"id": "rea-playwright-browser-scenario",
|
||||
"name": "REA Playwright browser scenario capture provider",
|
||||
"version": "1",
|
||||
"capabilities": ["capture_browser_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-playwright-electron-active",
|
||||
"name": "REA Playwright active Electron observation provider",
|
||||
"version": "1",
|
||||
"capabilities": ["capture_electron_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-v8-inspector",
|
||||
"name": "REA passive Node/Electron V8 Inspector provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"list_javascript_runtime_targets",
|
||||
"observe_javascript_runtime"
|
||||
]
|
||||
}
|
||||
],
|
||||
"setup_clients": [
|
||||
{
|
||||
"id": "claude_code",
|
||||
"display_name": "Claude Code",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "claude_desktop",
|
||||
"display_name": "Claude Desktop",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "codex",
|
||||
"display_name": "Codex",
|
||||
"format": "toml",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "cursor",
|
||||
"display_name": "Cursor",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "gemini_cli",
|
||||
"display_name": "Gemini CLI",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "windsurf",
|
||||
"display_name": "Windsurf",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "devin",
|
||||
"display_name": "Devin",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "opencode",
|
||||
"display_name": "OpenCode",
|
||||
"format": "opencode",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "antigravity",
|
||||
"display_name": "Antigravity",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "copilot_cli",
|
||||
"display_name": "GitHub Copilot CLI",
|
||||
"format": "copilot_cli",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "vscode",
|
||||
"display_name": "VS Code",
|
||||
"format": "vscode",
|
||||
"configuration": "managed"
|
||||
}
|
||||
],
|
||||
"cli": {
|
||||
"primary_count": 69,
|
||||
"commands": [
|
||||
"analyze",
|
||||
"analyze-javascript-application",
|
||||
"analyze-web-bundle",
|
||||
"build-reconstruction-obligation-ledger",
|
||||
"capabilities",
|
||||
"capture-browser-scenario",
|
||||
"capture-electron-scenario",
|
||||
"capture-native-ui-scenario",
|
||||
"capture-process",
|
||||
"capture-web-screenshot",
|
||||
"compare",
|
||||
"compare-application-versions",
|
||||
"compare-javascript-export-shapes",
|
||||
"compare-managed-members",
|
||||
"compare-process-captures",
|
||||
"compare-source-to-bundle",
|
||||
"compare-web-captures",
|
||||
"compare-web-screenshots",
|
||||
"decode-interface-builder",
|
||||
"decompile",
|
||||
"demangle-swift",
|
||||
"discover-webmcp-tools",
|
||||
"doctor",
|
||||
"evaluate-reconstruction-coverage",
|
||||
"evidence-export",
|
||||
"evidence-import",
|
||||
"extract-artifact",
|
||||
"function",
|
||||
"import-managed-reconstruction",
|
||||
"import-reference-source",
|
||||
"inspect",
|
||||
"inspect-artifact",
|
||||
"inspect-asset-catalog",
|
||||
"inspect-electron-page",
|
||||
"inspect-keyed-archive",
|
||||
"inspect-macho",
|
||||
"inspect-managed-artifact",
|
||||
"inspect-managed-members",
|
||||
"inspect-managed-native-boundaries",
|
||||
"inspect-native-api",
|
||||
"inspect-native-data-type",
|
||||
"inspect-native-dispatch-metadata",
|
||||
"inspect-native-instruction",
|
||||
"inspect-plist",
|
||||
"inspect-signature",
|
||||
"inspect-web-page",
|
||||
"instructions",
|
||||
"list-architectures",
|
||||
"list-browser-targets",
|
||||
"list-electron-targets",
|
||||
"list-javascript-runtime-targets",
|
||||
"observe-javascript-runtime",
|
||||
"observe-native-ui",
|
||||
"observe-web-session",
|
||||
"project-managed-application-graph",
|
||||
"providers",
|
||||
"reconcile-javascript-runtime",
|
||||
"resolve-native-call-targets",
|
||||
"search",
|
||||
"setup",
|
||||
"trace",
|
||||
"trace-application-feature",
|
||||
"trace-javascript-semantics",
|
||||
"trace-native-ui-action",
|
||||
"trace-native-values",
|
||||
"uninstall",
|
||||
"update",
|
||||
"verify-managed-native-boundaries",
|
||||
"xrefs"
|
||||
],
|
||||
"aliases": [
|
||||
{
|
||||
"name": "compare-bundles",
|
||||
"target": "compare"
|
||||
}
|
||||
]
|
||||
},
|
||||
"runtime_catalog": {
|
||||
"counts": {
|
||||
"cli_commands": 69,
|
||||
"mcp_tools": 116,
|
||||
"mcp_prompts": 6
|
||||
},
|
||||
"digests": {
|
||||
"tools_sha256": "7849b6f39bed200cfbf5f4d09ba3f9e0661824efb5753ae2edde8ef2fb66535e",
|
||||
"prompts_sha256": "4762db348821fec787a5b8c6423f2c48a71f6f0ec2afee97b16ad69030ec504b",
|
||||
"combined_sha256": "5bb3f994a3c4d0c87a34ff5aab4e28141b5fa6f3a98b5ae7e95686d85d9d983e",
|
||||
"providers_sha256": "2ae8b85b20be338739e500549d275e78e875aaa10235d2ca283f0ade7787da90"
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
-39
@@ -1,21 +1,29 @@
|
||||
# Static-analysis provider evaluation
|
||||
|
||||
Ghidra read-only analysis is available on Linux x64 and macOS x64/arm64.
|
||||
Install Ghidra 12.1.4 and a full 64-bit JDK 21 separately, then configure REA to
|
||||
use them. macOS installations need the matching native decompiler.
|
||||
This guide describes repository main. Check the [released package boundary](installation.md#released-package-and-main) when using npm.
|
||||
|
||||
REA imports one target into a temporary project and exposes 22 read-only
|
||||
Ghidra read-only analysis is available on Linux x64/arm64 and macOS x64/arm64.
|
||||
Install a Ghidra 12.1.x release and the 64-bit full JDK that release declares,
|
||||
then configure REA to use them. Current 12.1 releases require JDK 21 or newer
|
||||
and set no maximum. The bridge is verified with Ghidra 12.1.4 and JDK 21.
|
||||
Each installation needs the matching native decompiler.
|
||||
|
||||
REA imports one target into a temporary project and exposes 25 read-only
|
||||
operations after analysis completes. They cover inventories, search,
|
||||
decompilation, assembly, function metadata, resolved calls, references, control
|
||||
flow, instructions, and recovered types. Results identify the provider version,
|
||||
analysis settings, target digest, and any unavailable or incomplete facts.
|
||||
Ghidra does not expose Hopper's GUI or annotation operations.
|
||||
On Linux and macOS, `annotate_native_function` additionally edits names and entry
|
||||
comments atomically, verifies readback, and returns refreshed function analysis.
|
||||
Changes live only in the session database and leave executable bytes unchanged.
|
||||
Ghidra does not expose GUI controls; Windows P0 remains read-only.
|
||||
|
||||
Windows Ghidra analysis is unavailable, including on a correctly configured
|
||||
Ghidra/JDK host. Job Object process ownership, private runtime DACLs, and
|
||||
reparse-safe path checks are not implemented. Package and adapter tests do not
|
||||
establish real Windows analysis support. See [Windows Ghidra P0](windows-ghidra-p0.md)
|
||||
and [issue #527](https://github.com/morluto/rea/issues/527).
|
||||
Windows x64 P0 supports native x86 and x86-64 PE applications on local NTFS with
|
||||
bundled Job Object ownership, protected runtime DACLs, and handle-based path
|
||||
admission. Real ordinary-user verification covers all 25 read-only operations
|
||||
through the packaged CLI and MCP, target integrity, and cleanup. This remains
|
||||
an experimental boundary; see [Windows Ghidra P0](windows-ghidra-p0.md) and
|
||||
[issue #527](https://github.com/morluto/rea/issues/527).
|
||||
|
||||
[ADR-0001](adr/0001-provider-selection-and-analysis-profiles.md) fixes the
|
||||
provider registry, deterministic selection, target binding, analysis profile,
|
||||
@@ -41,18 +49,21 @@ follow.
|
||||
- Report GUI cursor/navigation and persistent mutation operations as unavailable
|
||||
until their semantics and project ownership are explicitly designed.
|
||||
- Verify real claims on Linux with at least two distinct source-owned binaries.
|
||||
Verify the Windows host claim with the deterministic source-owned x86-64 PE
|
||||
fixture and all admitted operations; compare normalized semantic facts rather
|
||||
Verify the Windows host claim with the deterministic source-owned x86 and x86-64 PE
|
||||
fixtures and all admitted operations; compare normalized semantic facts rather
|
||||
than provider-specific pseudocode text.
|
||||
|
||||
## Shipped foundation boundary
|
||||
|
||||
`GHIDRA_INSTALL_DIR` must identify an extracted official 12.1.4 release;
|
||||
optional `JAVA_HOME` must identify a 64-bit full JDK 21, otherwise doctor probes
|
||||
`java`/`javac` or `java.exe`/`javac.exe` from `PATH`. Supported Linux and macOS
|
||||
`GHIDRA_INSTALL_DIR` must identify an extracted Ghidra 12.1.x release. Optional
|
||||
`JAVA_HOME` must identify a 64-bit full JDK inside that installation's
|
||||
`application.java.min` and `application.java.max` (JDK 21 or newer, with no
|
||||
maximum, when the release leaves those at the 12.1 defaults). Otherwise doctor
|
||||
probes `java`/`javac` or `java.exe`/`javac.exe` from `PATH`. Supported Linux and macOS
|
||||
hosts accept compatible ELF, PE, and Mach-O executable targets. Host admission
|
||||
and target compatibility are separate checks. The intended Windows P0 accepts
|
||||
only native x86-64 PE applications; it remains blocked before launch.
|
||||
and target compatibility are separate checks. Windows x64 P0 admits only native,
|
||||
non-managed, non-DLL x86 and x86-64 PE applications on fixed local NTFS, with the packaged
|
||||
native authority available before launch.
|
||||
|
||||
The launcher creates one ephemeral runtime root with project,
|
||||
home/cache/config/data/temp, logs, descriptor, endpoint, target snapshot, and
|
||||
@@ -63,18 +74,21 @@ On Linux, the mode-0600 descriptor carries the random token without
|
||||
exposing it in argv or environment and the Java bridge binds a mode-restricted
|
||||
Unix socket. macOS uses the same local transport. The bridge reports actual
|
||||
Ghidra/language/compiler/analysis/import-digest metadata and accepts only
|
||||
authenticated `ping`, `shutdown`, ten inventory methods, and twelve
|
||||
function-analysis methods. Close, cancellation, timeout, malformed protocol,
|
||||
authenticated `ping`, `shutdown`, thirteen read-only inventory/load-image methods,
|
||||
and twelve function-analysis methods. Linux/macOS also admits atomic function
|
||||
annotation changes in the ephemeral database. Close, cancellation, timeout, malformed protocol,
|
||||
or process exit stops the owned process resources, closes the socket, and
|
||||
removes the runtime root.
|
||||
|
||||
The experimental Windows transport uses authenticated IPv4 loopback with a
|
||||
token-free endpoint record, but cannot start while the native authority is
|
||||
unavailable. Its `taskkill` cleanup code does not establish Job Object ownership,
|
||||
private DACLs, or reparse-safe paths.
|
||||
token-free endpoint record. The bundled native authority establishes Job Object
|
||||
ownership, protected runtime DACLs, and handle-based path admission before
|
||||
launch. If the bundle or an admission constraint is unavailable, the provider
|
||||
remains unavailable; a `taskkill` fallback does not establish that authority.
|
||||
|
||||
The provider catalog lists the 22 Ghidra operations. GUI cursor,
|
||||
navigation, and mutation operations remain absent; the router therefore
|
||||
The provider catalog lists 25 read-only Ghidra operations and the Linux/macOS
|
||||
session annotation operation. GUI cursor/navigation and persistent mutations
|
||||
remain unavailable; Windows P0 admits no database mutation. The router therefore
|
||||
reports them unavailable instead of borrowing Hopper semantics or inferring
|
||||
capability from a successful import.
|
||||
|
||||
@@ -107,7 +121,7 @@ capability from a successful import.
|
||||
| Result extent | Function instruction scans and native API boundary observations remain complete. P-code flow is the explicit bounded exception; its count fields distinguish exact retained counts from lower-bound or known-omitted counts. Caller cancellation and provider failures remain distinct. |
|
||||
|
||||
`npm run verify:ghidra` compiles the versioned C oracle into debug and stripped
|
||||
host-native targets (x86-64 ELF on Linux x64 or Mach-O on macOS), plus a native
|
||||
host-native targets (x86-64/AArch64 ELF on Linux or Mach-O on macOS), plus a native
|
||||
DWARF 4 type-layout object. It proves the admitted operations, external functions, resolved thunks, exports, stripped-name
|
||||
behavior, direct and targetless indirect calls, typed references, strings/xrefs,
|
||||
multi-block CFG, bounded p-code def-use/effect links, semantic enhanced workflows, cancellation, startup deadlines,
|
||||
@@ -122,12 +136,14 @@ Mach-O target coverage. It requires `clang`, LLD, and `lld-link`; set
|
||||
these tools before compilation. Keeping this matrix separate lets Linux
|
||||
host/provider acceptance run with only the host compiler.
|
||||
|
||||
`npm run verify:ghidra:windows` remains blocked by the missing Windows native
|
||||
authority. After those controls are implemented, the lane must check the
|
||||
source-owned native x86-64 PE fixture, all read-only operations,
|
||||
target/snapshot/import SHA-256 linkage, and project, endpoint, process, and
|
||||
runtime cleanup. The existence of this verifier is not evidence that Windows
|
||||
Ghidra analysis works.
|
||||
`npm run verify:ghidra:windows` and its `-- --x86` variant check the source-owned native x86 and x86-64 PE
|
||||
fixture, all 25 read-only operations, target/snapshot/import SHA-256 linkage,
|
||||
and project, endpoint, process, and runtime cleanup. The independent native
|
||||
lane checks DACLs, handle admission, cancellation, and Job Object lifecycle.
|
||||
`npm run verify:ghidra:windows:package` packs and installs REA into an isolated
|
||||
prefix, then checks ordinary-user CLI/MCP operations against canonical contracts.
|
||||
All lanes require matching native controls; package startup alone establishes
|
||||
only package compatibility.
|
||||
|
||||
## Shared provider-process foundation
|
||||
|
||||
@@ -138,8 +154,8 @@ request cancellation and lifecycle-deadline cleanup, bounded stdout and stderr r
|
||||
exact byte counts, process-exit diagnostics, and bounded TERM-to-KILL shutdown.
|
||||
Reusable fixtures exercise exit, timeout, cancellation, graceful termination,
|
||||
forced termination, double-close, spawn failure, and resource release. Windows
|
||||
process-tree termination code exists, but the missing Job Object and private
|
||||
runtime controls prevent it from establishing an available Ghidra session.
|
||||
Ghidra uses its native Job Object, private runtime, and path-admission boundary;
|
||||
generic process-tree termination alone cannot establish provider availability.
|
||||
|
||||
The foundation does not define a bridge schema, socket framing, health payload,
|
||||
analysis model, or shutdown acknowledgement. Hopper keeps its authenticated
|
||||
@@ -149,12 +165,12 @@ only the generic process mechanisms.
|
||||
|
||||
## Shortlist
|
||||
|
||||
| Provider | License / automation surface | What it brings | REA fit and blockers |
|
||||
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Ghidra](https://github.com/NationalSecurityAgency/ghidra) | Apache-2.0 source license; `analyzeHeadless`, Java APIs, and PyGhidra | Static analysis, multiple processors and formats, scripting, and project/database workflows | 22 read-only operations shipped on Linux/macOS with installation checks, temporary projects, a serial API queue, and real ELF/PE/Mach-O conformance. Windows analysis is blocked; GUI and mutation operations are unavailable. |
|
||||
| [Rizin](https://github.com/rizinorg/rizin) / [rz-pipe](https://github.com/rizinorg/rz-pipe) | Rizin repository contains LGPL-3.0 and GPL-3.0 components; `rizin`, `rz-bin`, and language bridges through `rzpipe` | Portable CLI analysis, disassembly/debugging, many architectures and file formats, JSON command output | Good candidate for a process-backed Linux provider and fast metadata fallback. License/component inventory must be preserved; command output needs version-pinned parsers and semantic conformance before evidence is trusted. |
|
||||
| [LIEF](https://github.com/lief-project/LIEF) | Apache-2.0; C++, Python, and other bindings | Deterministic parsing and modification of ELF, PE, Mach-O, COFF, and related executable formats; headers, sections, symbols, relocations, and functions | Best near-term complement, not a decompiler replacement. It can cover format metadata and artifact evidence without a long-lived analysis process; function semantics, pseudocode, CFG, and cross-reference parity remain out of scope unless separately demonstrated. |
|
||||
| [Binary Ninja](https://docs.binary.ninja/dev/index.html) | API/documentation components are MIT, while the analysis product is licensed by edition; commercial, Ultimate, or Headless license is required for headless automation | Python/Core/C++/Rust APIs, headless loading, IL layers, function analysis, plugins, and configurable analysis | Strong technical fit for a native provider, especially function dossiers. Commercial licensing, license-secret handling, native runtime packaging, and multithreaded lifecycle rules are material deployment blockers. |
|
||||
| Provider | License / automation surface | What it brings | REA fit and blockers |
|
||||
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Ghidra](https://github.com/NationalSecurityAgency/ghidra) | Apache-2.0 source license; `analyzeHeadless`, Java APIs, and PyGhidra | Static analysis, multiple processors and formats, scripting, and project/database workflows | 25 read-only operations shipped on Linux/macOS with installation checks, temporary projects, a serial API queue, and real ELF/PE/Mach-O conformance. Experimental Windows x64 P0 supports native x86 and x86-64 PE on local NTFS with bundled native controls and ordinary-user CLI/MCP verification; Linux/macOS additionally support atomic session function annotations; Windows mutation and all Ghidra GUI operations are unavailable. |
|
||||
| [Rizin](https://github.com/rizinorg/rizin) / [rz-pipe](https://github.com/rizinorg/rz-pipe) | Rizin repository contains LGPL-3.0 and GPL-3.0 components; `rizin`, `rz-bin`, and language bridges through `rzpipe` | Portable CLI analysis, disassembly/debugging, many architectures and file formats, JSON command output | Good candidate for a process-backed Linux provider and fast metadata fallback. License/component inventory must be preserved; command output needs version-pinned parsers and semantic conformance before evidence is trusted. |
|
||||
| [LIEF](https://github.com/lief-project/LIEF) | Apache-2.0; C++, Python, and other bindings | Deterministic parsing and modification of ELF, PE, Mach-O, COFF, and related executable formats; headers, sections, symbols, relocations, and functions | Best near-term complement, not a decompiler replacement. It can cover format metadata and artifact evidence without a long-lived analysis process; function semantics, pseudocode, CFG, and cross-reference parity remain out of scope unless separately demonstrated. |
|
||||
| [Binary Ninja](https://docs.binary.ninja/dev/index.html) | API/documentation components are MIT, while the analysis product is licensed by edition; commercial, Ultimate, or Headless license is required for headless automation | Python/Core/C++/Rust APIs, headless loading, IL layers, function analysis, plugins, and configurable analysis | Strong technical fit for a native provider, especially function dossiers. Commercial licensing, license-secret handling, native runtime packaging, and multithreaded lifecycle rules are material deployment blockers. |
|
||||
|
||||
## Recommended order
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Recorded crash evidence
|
||||
|
||||
`inspect_recorded_crash` / `rea inspect-recorded-crash` inspect a caller-selected
|
||||
Linux ELF64 x86-64 little-endian core independently of an active binary target.
|
||||
|
||||
```sh
|
||||
export REA_PWNTOOLS_PYTHON=/absolute/existing/venv/bin/python
|
||||
rea inspect-recorded-crash ./crash.core --json
|
||||
```
|
||||
|
||||
The selected Python needs unchanged pwntools 4.15.0, pyelftools 0.33 and Unicorn 2.1.2.
|
||||
REA does not install these dependencies. ELFFile/its original ELF structures and
|
||||
pwntools' original Linux amd64 ctypes provide parsing and ABI layout. The adapter
|
||||
adds bounds, representation preservation and lifecycle ownership.
|
||||
The initial Linux note profile accepts System V/unspecified and GNU/Linux OSABI
|
||||
with ABI version zero. Other declared ABIs return `unsupported_target` before
|
||||
note interpretation; an unspecified OSABI alone does not prove OS origin.
|
||||
|
||||
## Recorded observations
|
||||
|
||||
- Artifact path, original SHA-256 and size; full normalized result and Evidence
|
||||
are inline through CLI and MCP.
|
||||
- Original program headers and recorded segment file/memory ranges. Undumped
|
||||
memory and current executable/library identity remain unknown.
|
||||
- Every note's original identity, owner/descriptor bytes and source ranges,
|
||||
including unfamiliar owners and note kinds.
|
||||
- Every exact Linux CORE PRSTATUS note as a separate recorded thread, with
|
||||
historical PID, signal and lossless uint64 registers bound to source bytes.
|
||||
Each supported note has exactly one interpretation; each thread contains the
|
||||
complete 27-register ABI set, with names bound to their original ABI offsets.
|
||||
Physical note ranges cannot overlap or leave segment gaps. ELF and note headers
|
||||
are independently bound to the snapshot, including extended program counts.
|
||||
- Signed SIGINFO number/code/errno. Initially only SIGSEGV codes 1/2 establish the
|
||||
fault-address union; other variants retain raw bytes and unknown meaning.
|
||||
|
||||
Historical PIDs are metadata. They never select a live process or authorize
|
||||
attach/control. Missing notes do not establish absence at the crash; note
|
||||
interpretation completeness and signal-thread association remain unknown.
|
||||
|
||||
## Optional debugger mapping context
|
||||
|
||||
```sh
|
||||
export REA_PWNDBG_GDB=/absolute/existing/gdb
|
||||
export REA_PWNDBG_GDBINIT=/absolute/unchanged/pwndbg/gdbinit.py
|
||||
export REA_PWNDBG_VENV_PATH=/absolute/existing/pwndbg-venv
|
||||
rea inspect-recorded-crash ./crash.core --debugger-context --json
|
||||
```
|
||||
|
||||
MCP uses `{ "path": "/artifacts/crash.core", "include_debugger_context": true }`.
|
||||
Omitting this meaningful facet requests only recorded evidence. Requested but
|
||||
unavailable context returns an actionable failure; it never silently claims
|
||||
debugger coverage. GDB defaults to `/usr/bin/gdb`; the plugin and virtual
|
||||
environment require explicit absolute paths. Initial local verification covers
|
||||
Linux x64, GDB 13.1 and unchanged pwndbg 2026.09.15.
|
||||
|
||||
The outer provider owns both processes and one stable core snapshot. An owned
|
||||
Python launcher lowers inherited soft limits before replacing itself with GDB.
|
||||
GDB starts without initfiles, auto-load, debuginfod or automatic shared-library
|
||||
loading. Owned empty library search paths and caches are used, with plugin
|
||||
updates disabled. Fixed package code verifies a core connection and no paired
|
||||
executable before querying mapping candidates. No caller GDB command language is
|
||||
exposed. The exact observed GDB/plugin version strings are retained.
|
||||
|
||||
Mapping names are producer display metadata, with unknown current file identity.
|
||||
Map permissions are derived from reported flags; zero/unfamiliar flags leave
|
||||
permissions unknown. These maps do not establish page contents or an executable
|
||||
match. Core source bytes remain available independently of this enrichment.
|
||||
An Evidence envelope containing these maps is `derived`; inspection without
|
||||
debugger context is `observed`.
|
||||
|
||||
## Bounds and verification
|
||||
|
||||
Initial Linux x64 limits: 32 MiB input, 64 MiB decoder reply, 1 MiB diagnostics per
|
||||
stage, 30 seconds owned wall deadline per stage, 3 GiB address-space soft ceiling
|
||||
and 30 seconds CPU soft ceiling. Tighter inherited soft/hard limits are retained;
|
||||
hard limits are not raised. Complete oversized results fail explicitly. These
|
||||
process limits do not constitute a sandbox for caller-supplied engine code.
|
||||
|
||||
Resource exit statuses need matching private failure-branch markers. Unmarked
|
||||
launcher failures retain their actual status/output and unknown resource cause.
|
||||
Cancellation, output truncation and independent owned cleanup remain explicit.
|
||||
|
||||
`npm run verify:recorded:crash` uses source-owned fixtures and caller-supplied
|
||||
GDB/Python/pwndbg/strace. Fixture generation explicitly launches its owned test
|
||||
program; subsequent inspection only reads the resulting core. Real acceptance
|
||||
checks recorded threads, high registers, missing/malformed notes, opaque owners,
|
||||
CLI/MCP parity, optional debugger context and historical PID collision with an
|
||||
owned live sentinel. Original inputs and host configuration remain unchanged.
|
||||
@@ -0,0 +1,210 @@
|
||||
# Releasing REA
|
||||
|
||||
Pushes to main automatically open or refresh a Release Please PR with the next
|
||||
version and changelog. Review its migration notes and wait for its current CI
|
||||
checks to pass. Merging that PR into main starts publication automatically.
|
||||
|
||||
The Release workflow validates the merged release metadata and ancestry before
|
||||
creating a tag. npm and MCP Registry publication use the exact reviewed merge
|
||||
SHA returned by Release Please. Later main commits cannot change that source.
|
||||
Ordinary main pushes and closed, unmerged PRs cannot publish packages. Check
|
||||
both publication jobs and the published-package canary; a merged PR or GitHub
|
||||
tag alone does not establish that publication finished. Retry failed jobs in
|
||||
the original run after a partial publication.
|
||||
|
||||
For a release that needs an independently frozen application checkpoint, use
|
||||
the optional manual path below. It retains explicit prepare and publish phases.
|
||||
|
||||
## 1. Select a source for a manual checkpoint
|
||||
|
||||
Use the next minor version proposed by Release Please, including releases with
|
||||
breaking changes. Record the full source SHA and create `release/VERSION` at
|
||||
that commit. The selected version and Release Please's proposed version must
|
||||
agree before publication.
|
||||
|
||||
When choosing the manual path, leave the automatic main release PR unmerged.
|
||||
Select the application commit from main, create the frozen branch, and prepare
|
||||
its own release PR using the steps below. Merging the main release PR starts
|
||||
automatic publication instead. Close the superseded main PR after the manual
|
||||
release has published and its metadata has been synchronized back to main.
|
||||
|
||||
`release/VERSION` also sets the expected version for the workflow. Use an exact
|
||||
SemVer such as `release/6.1.0` or `release/6.1.0-rc.1`, without build metadata.
|
||||
The checkpoint validator rejects a candidate whose package, lockfile, manifest,
|
||||
registry metadata or changelog disagrees with that version before creating a tag.
|
||||
|
||||
### Compatibility and version selection
|
||||
|
||||
REA uses Release Please's `always-bump-minor` strategy: every release increments
|
||||
minor and resets patch, including releases with breaking changes. For example,
|
||||
the next release after 6.0.0 is 6.1.0. Version numbers use the SemVer format,
|
||||
but a minor increment does not promise backward compatibility. Review the
|
||||
changelog's breaking-change section and migration notes before upgrading.
|
||||
|
||||
The supported public surface includes documented CLI commands/options, MCP tool
|
||||
names and input/result contracts, saved evidence formats and supported runtime
|
||||
requirements. Before adding `!` or a `BREAKING CHANGE` footer, a PR must identify
|
||||
a previously valid call or configuration that will fail or change meaning,
|
||||
explain why compatibility cannot be preserved, and give its migration. Prefer
|
||||
optional additions, compatibility adapters and a documented deprecation period.
|
||||
Keep breaking markers so the bot includes migration notes without incrementing
|
||||
major. Maintainers review this impact before merging; the bot parses markers
|
||||
and cannot infer compatibility.
|
||||
|
||||
For example, adding an optional inspection tool is a minor change. Rejecting a
|
||||
relative MCP path previously accepted by the public contract is breaking;
|
||||
rejecting an input that the existing contract already prohibited is a fix.
|
||||
Changing the version number alone does not restore compatibility.
|
||||
|
||||
For example, after selecting a reviewed commit for 5.0.0:
|
||||
|
||||
```bash
|
||||
git fetch origin main
|
||||
git branch release/5.0.0 SOURCE_SHA
|
||||
git push origin release/5.0.0
|
||||
```
|
||||
|
||||
Replace `SOURCE_SHA` with the selected full commit SHA. The checkpoint must
|
||||
include this release workflow and CI support for release branches. For an
|
||||
older checkpoint, backport only the release infrastructure first and record
|
||||
that additional commit. Do not merge later implementation changes into the
|
||||
release branch or rebase the candidate onto a moving main.
|
||||
|
||||
## 2. Prepare the bot PR
|
||||
|
||||
Run the workflow definition from main and select the frozen source separately:
|
||||
|
||||
```bash
|
||||
gh workflow run release.yml --ref main \
|
||||
-f release_branch=release/5.0.0 -f phase=prepare
|
||||
```
|
||||
|
||||
Release Please targets that branch and prepares its version, changelog, and
|
||||
registry metadata. The workflow uses an optional `RELEASE_PLEASE_TOKEN`
|
||||
repository secret and otherwise uses the built-in `GITHUB_TOKEN`. A dedicated
|
||||
GitHub App or personal access token can start PR CI automatically. With the
|
||||
built-in token, `opened`, `synchronize`, and `reopened` PR events create runs
|
||||
that require a maintainer with write access to approve them. See
|
||||
[GitHub's workflow trigger documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow).
|
||||
|
||||
The generation step normalizes the product catalog. Preparation cannot create
|
||||
a GitHub release or publish a package. Record the final bot PR head after
|
||||
normalization, then approve that head's blocked runs from the PR page. Updates
|
||||
to the bot branch can create new approval-required runs; approval of an older
|
||||
head does not verify the normalized candidate. Wait for the final head's CI.
|
||||
|
||||
The validator reads the actual ancestry range from the published baseline tag
|
||||
to the selected checkpoint, including visible first-parent Conventional Commits
|
||||
and the PR titles in GitHub's default merge messages. The report preserves the
|
||||
original merge subject alongside the extracted Conventional Commit title.
|
||||
This matters after merging a side-branch release back into a newer main:
|
||||
Release Please's chronological history cutoff can omit unreleased mainline
|
||||
commits. Under `always-bump-minor`, unreleased breaking markers allow a minor
|
||||
increment and still require references in the new release's breaking-change
|
||||
migration section. Checkpoints using the default strategy retain the major
|
||||
increment requirement. Other omitted entries are reported for review.
|
||||
Historical notes from an older release cannot satisfy
|
||||
the new release's migration check. This audits declared markers and references;
|
||||
it does not prove API compatibility or the quality of a migration explanation.
|
||||
|
||||
If preparation reports a mismatch or omitted breaking changes, inspect the
|
||||
recorded ancestry report, correct the release PR's version artifacts and notes,
|
||||
regenerate documentation, and review/test its final head. Do not repeatedly
|
||||
prepare over manual corrections. Publication independently checks the merged
|
||||
candidate again before Release Please can create a tag.
|
||||
|
||||
Review the candidate's version, notes, generated metadata, and package
|
||||
contents. Wait for the candidate's CI and relevant real-provider checks.
|
||||
Routine local iterations need focused checks; CI owns full deterministic
|
||||
coverage and platform lanes. If an artifact or real-provider check is
|
||||
unavailable, report that limit before deciding to publish.
|
||||
|
||||
Merge the reviewed PR into `release/5.0.0` with its head SHA matched. Further
|
||||
main commits do not change this candidate. A necessary release fix belongs
|
||||
on the release branch, must be reviewed and tested, and establishes a new
|
||||
recorded checkpoint.
|
||||
|
||||
## 3. Publish the reviewed merge
|
||||
|
||||
After the release PR has merged:
|
||||
|
||||
```bash
|
||||
gh workflow run release.yml --ref release/5.0.0 \
|
||||
-f release_branch=release/5.0.0 -f phase=publish
|
||||
```
|
||||
|
||||
Publication creates the release from the merged bot PR without preparing or
|
||||
updating another PR. Both npm and MCP Registry jobs check out Release Please's
|
||||
exact release SHA. They do not build the current main tip or a mutable branch.
|
||||
Stable versions publish to npm's `latest` tag; prerelease versions publish to
|
||||
the `next` tag so they cannot replace the stable install by default.
|
||||
The publish dispatch runs from the frozen release branch so npm's provenance
|
||||
records the actual release commit. Before Release Please creates a tag, the
|
||||
workflow accepts only `prepare` or `publish` and requires the selected branch
|
||||
tip to equal the dispatch SHA. Registry jobs run only during `publish`. After
|
||||
a tag exists, a mismatch between that tag's SHA and the dispatch SHA stops
|
||||
both registry publishes.
|
||||
See [npm's provenance implementation](https://github.com/npm/cli/blob/v11.16.0/workspaces/libnpmpublish/lib/provenance.js)
|
||||
for the use of GitHub's workflow ref and commit SHA.
|
||||
|
||||
The workflow builds the bundled Windows controls and verifies the packaged
|
||||
artifact before npm publication. It then verifies the published CLI, the
|
||||
capability-scoped MCP catalog, and the isolated package update path before
|
||||
publishing MCP Registry metadata.
|
||||
|
||||
Record these outcomes separately:
|
||||
|
||||
- GitHub release and tag, including the resolved commit SHA.
|
||||
- npm's exact version and integrity, with the published-package canary passed.
|
||||
- MCP Registry's exact server version and matching npm package version.
|
||||
|
||||
A GitHub tag alone does not establish npm or MCP Registry publication.
|
||||
|
||||
## 4. Sync metadata back to main
|
||||
|
||||
Manual checkpoint synchronization is part of completing that release. Finish it
|
||||
before cutting the next checkpoint; otherwise main retains the previous
|
||||
release baseline and can propose an already-published version again.
|
||||
|
||||
After publication, open a PR from the release branch back to main. Preserve
|
||||
main's later implementation changes and regenerate ignored build outputs from
|
||||
the combined contracts with the released package version.
|
||||
Review and test this synchronization PR, then use a merge commit so the release
|
||||
tag remains in main's ancestry. Keep the released tag unchanged.
|
||||
|
||||
The synchronization must include `.release-please-manifest.json`,
|
||||
`package.json`, both root versions in `package-lock.json`, `CHANGELOG.md`,
|
||||
`server.json`, and the versioned documentation examples. Run
|
||||
`npm run docs:generate`, `npm run docs:check`, and the release configuration
|
||||
tests against the combined tree. After the merge, verify that the released
|
||||
tag is an ancestor of main:
|
||||
|
||||
```bash
|
||||
git fetch origin main --tags
|
||||
git merge-base --is-ancestor rea-agents-5.0.0 origin/main
|
||||
```
|
||||
|
||||
Close the superseded main proposal before merging the synchronization PR.
|
||||
That merge's main push opens a proposal for the following release using the
|
||||
updated baseline. Future releases can use the automatic main PR or repeat the
|
||||
manual checkpoint procedure; automatic proposals do not move frozen branches.
|
||||
Preparation generates and validates the catalog, portable conformance
|
||||
projections, and packaged skill from the candidate checkout without committing
|
||||
them. The checkpoint validator binds tracked version authority to the source
|
||||
SHA; generated-catalog and package checks validate the derived representation.
|
||||
No generated-metadata workflow pushes follow-up commits onto feature or release
|
||||
branches.
|
||||
|
||||
## Partial publication and retries
|
||||
|
||||
Inspect the failed job and public registry state before retrying. When npm is
|
||||
already published, the npm job verifies that exact version instead of
|
||||
publishing it again. Re-run failed jobs in the original publication run so its
|
||||
release SHA and outputs stay fixed. If only MCP publication failed, retry that
|
||||
job after checking that the npm canary succeeded.
|
||||
|
||||
A branch tip that moved after dispatch fails before Release Please creates a
|
||||
tag. Do not dispatch a fresh publish phase to repair an already-created
|
||||
release: Release Please will not create the same release again. Do not move
|
||||
the tag, delete the release, or unpublish npm as a retry. A defective public
|
||||
package requires a reviewed correction and a new version.
|
||||
+42
-15
@@ -20,6 +20,30 @@ validation, provider prerequisites, target identity, cancellation, and owned
|
||||
resource cleanup remain part of each tool's contract. Setup still discloses
|
||||
installation changes and requires approval before writing or installing.
|
||||
|
||||
## Planned work
|
||||
|
||||
The [website guides](https://rea.tools/guides/) and
|
||||
[generated catalog](product-catalog.json) describe available workflows.
|
||||
Current development priorities are:
|
||||
|
||||
- Keep generated metadata and narrative documentation aligned with tools,
|
||||
providers, setup options and releases.
|
||||
- Expand native architecture, type and indirect-call verification across
|
||||
Hopper and Ghidra.
|
||||
- Connect additional static extractors and runtime observations to
|
||||
cross-layer feature traces.
|
||||
- Improve obfuscated .NET comparisons and links between managed findings
|
||||
and verified native analysis.
|
||||
- Extend process, protocol, filesystem, reconnect and build-comparison
|
||||
coverage, plus browser and Electron scenario actions.
|
||||
- Evaluate native runtime observation through LLDB, Frida, system logs and
|
||||
API tracing, and additional tools and targets such as Binary Ninja, Rizin,
|
||||
LIEF, Windows-native workflows, mobile applications and firmware.
|
||||
|
||||
Provider additions and platform support require the corresponding real
|
||||
verification lanes; see [testing](testing.md) and
|
||||
[provider evaluation](provider-evaluation.md).
|
||||
|
||||
## Remaining evidence and provider work
|
||||
|
||||
The [platform roadmap](https://github.com/morluto/rea/issues/32),
|
||||
@@ -43,12 +67,16 @@ REA setup lets you select agent integration and optional Hopper installation.
|
||||
It installs the bundled workflow, configures detected agents, and can save
|
||||
verified paths for an existing Ghidra installation. It configures Claude Code,
|
||||
Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity,
|
||||
GitHub Copilot CLI, and VS Code using each client's configuration format.
|
||||
GitHub Copilot CLI, Command Code, VS Code, and Grok Build using each client's configuration format. Grok Bot is detected locally, but its connector is added from the Grok Bot chat because the signed-in account store is not a data-directory file.
|
||||
|
||||
Ghidra analysis supports Linux x64 and macOS x64/arm64 with Ghidra 12.1.4 and a
|
||||
64-bit full JDK 21. macOS also requires the matching native decompiler. The
|
||||
adapter exposes ten inventory operations and twelve function-analysis
|
||||
operations, for 22 read-only operations total. Approved setup saves verified
|
||||
Ghidra analysis supports Linux x64/arm64 and macOS x64/arm64 with Ghidra 12.1.x and the
|
||||
64-bit full JDK that installation declares. Current 12.1 releases require JDK 21
|
||||
or newer and set no maximum; the bridge is verified with Ghidra 12.1.4 and JDK 21.
|
||||
Each installation requires the matching native decompiler. The
|
||||
adapter exposes thirteen inventory operations and twelve function-analysis
|
||||
operations, for 25 read-only operations total. Linux and macOS additionally support
|
||||
atomic function-name and entry-comment edits with refreshed analysis; metadata
|
||||
is discarded on close and executable bytes stay unchanged. Approved setup saves verified
|
||||
installation paths in agent configurations without installing or changing
|
||||
Ghidra or Java.
|
||||
|
||||
@@ -57,11 +85,11 @@ a native type-layout object. A separate lane covers AArch64 ELF, PE, and
|
||||
Mach-O cross-target fixtures. See [testing](testing.md) for the prerequisites
|
||||
and exact commands.
|
||||
|
||||
Windows Ghidra operations remain unavailable until REA implements verified
|
||||
Job Object process ownership, private runtime DACLs, and reparse-safe path
|
||||
admission. Windows package and adapter tests do not establish a usable real
|
||||
Ghidra session. The [Windows P0 guide](windows-ghidra-p0.md) describes the
|
||||
intended boundary and remaining requirements.
|
||||
Experimental Windows x64 P0 now supplies bundled Job Object ownership,
|
||||
protected runtime DACLs, and handle-based admission for native x86 and x86-64 PE
|
||||
applications on local NTFS. Real ordinary-user CLI/MCP verification covers
|
||||
all 25 read-only operations and cleanup. The [Windows P0 guide](windows-ghidra-p0.md)
|
||||
describes this boundary and unverified broader coverage.
|
||||
|
||||
## Ghidra maintenance boundary
|
||||
|
||||
@@ -71,11 +99,10 @@ not expected to match; unresolved targetless flow remains unknown. Automatic
|
||||
Ghidra acquisition, if ever added, remains a separately planned and approved
|
||||
related-tool change; setup must never install Java.
|
||||
|
||||
Before enabling Windows Ghidra operations, add current-user-only DACL creation and
|
||||
readback, handle-based reparse-point-safe path authority, a DACL-protected IPC
|
||||
backend, and Job Object assignment before provider execution. Process capture,
|
||||
Hopper, and broad filesystem-sensitive workflows remain separate Windows
|
||||
projects rather than implied parity.
|
||||
Maintain Windows P0 with independent DACL readback, handle-based admission,
|
||||
private authenticated IPC, and Job Object assignment before provider execution.
|
||||
Process capture, Hopper, and broad filesystem-sensitive workflows remain
|
||||
separate Windows projects; P0 does not imply their parity.
|
||||
|
||||
## Capability-selective setup
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user