- FlowTracker एक Java agent है जो ट्रैक करता है कि Java प्रोग्राम डेटा को कैसे पढ़ते, बदलते और लिखते हैं, और दिखाता है कि output किस input, file, network या code constant से आया है
- यह चल रहे program को observe करके file और network I/O दिखाता है, और खास तौर पर input और output के correspondence को track करके यह समझने में मदद करता है कि Java program का output क्या मायने रखता है और वह क्यों बना
- Spring PetClinic demo में HTTP response के headers, Thymeleaf template, database values और SQL insert script तक follow करते हुए software stack की कई layers को explore किया जा सकता है
- अंदरूनी तौर पर यह JVM loading time पर bytecode instrument करता है, और JDK method hooks, data flow analysis,
ThreadLocal-based call tracking,ClassOriginTrackerको combine करके strings, chars और bytes पर केंद्रित origin mapping बनाए रखता है - मौजूदा स्थिति production-ready से ज्यादा proof of concept के करीब है; कुछ example programs में यह अच्छी तरह चला, लेकिन सभी programs के लिए suitable नहीं है और बड़े overhead की वजह से execution speed काफी धीमी हो जाती है
FlowTracker क्या track करता है
- FlowTracker एक Java agent है जो Java program के अंदर data कैसे पढ़ा, pass, transform और write किया जाता है, इसे track करता है
- यह सिर्फ files और network I/O दिखाने तक सीमित नहीं है, बल्कि program का output किस input से आया है, यह connect करके दिखाता है
- इसका उद्देश्य यह समझने में मदद करना है कि Java program का output क्या मायने रखता है, और program ने वह output क्यों लिखा
- फिलहाल project इस perspective से program behavior देखने पर किस तरह की insights मिल सकती हैं, इसे explore करने वाला proof-of-concept है
Demo: Spring PetClinic में HTTP response की origin tracking
- FlowTracker PetClinic demo browser में यह देखने देता है कि Spring PetClinic HTTP request को कैसे process करता है और templates व database data के आधार पर HTML page कैसे generate करता है
- Screen पर PetClinic द्वारा network पर भेजा गया HTTP response दिखता है, और response body के किसी हिस्से पर click करने से नीचे वाले view में देखा जा सकता है कि वह हिस्सा कहां से आया
- Left tree या mobile में bottom-left button से tracked inputs/origins या outputs/sinks चुने जा सकते हैं
-
HTTP processing layer
"HTTP/1.1"या HTTP header पर click करने से दिखता है कि response का यह हिस्साorg.apache.coyotepackage की Apache Coyote class ने generate किया था- FlowTracker दिखाता है कि किस code ने कौन-सा output बनाया
-
Thymeleaf template layer
"html"या"head"जैसे HTML tag names पर click करने से दिखता है कि संबंधित HTML हिस्साlayout.htmlfile से आया थाlayout.htmlपर click करने के बाद नीचे के color+button को दबाने से उस file से आए सभी हिस्से एक ही color में दिखते हैं- नीचे scroll करने पर पता चलता है कि response का कुछ हिस्सा दूसरी file
ownerDetails.htmlसे आया है <या>character पर click करने से दिखता है कि वह character Thymeleaf template library द्वारा लिखा गया था
-
Database value की origin
- HTML page की table में database से आई जानकारी शामिल है
- Table में
Georgeपर click करने से यह सिर्फ इतना नहीं दिखता कि value database से आई है, बल्कि इसे शुरुआत में database में value insert करने वाली SQL script तक track किया जाता है - इस demo में SQL script तक tracking इसलिए होती है क्योंकि in-memory database का उपयोग किया गया, जिससे database content JVM के बाहर नहीं गया
MySQL demo और framework independence
- वही PetClinic demo MySQL database के साथ चलाने पर values database connection point तक track होती हैं
- इस case में पहले value बनाने के लिए भेजी गई SQL query और MySQL JDBC driver database से कैसे communicate करता है, उसकी details देखी जा सकती हैं
- FlowTracker PetClinic mysql demo यह भी दिखाता है कि FlowTracker database SSL connection के जरिए भेजे गए decrypted content को intercept करता है
- Spring PetClinic सिर्फ एक example है; FlowTracker किसी खास framework या library पर निर्भर नहीं है
- javac demo Java compiler को observe करके यह दिखाता है कि generated class file format और उसके अंदर के bytecode को समझने में FlowTracker कैसे मदद करता है
उपयोग का तरीका और सावधानियां
- अभी FlowTracker production-ready होने की बजाय proof-of-concept के ज्यादा करीब है
- कई example programs में यह अच्छी तरह चला, लेकिन हर program में अच्छी तरह चलेगा, इसकी guarantee नहीं है
- यह काफी overhead जोड़ता है, इसलिए program execution बहुत धीमा हो जाता है
- इस्तेमाल की प्रक्रिया:
- Github releases pages से
flowtracker-*.jaragent jar download करें - Java command line में
-javaagent:path/to/flowtracker.jarजोड़ें - FlowTracker में बाधा डालने वाली कुछ JVM optimizations को बंद करने के लिए
java -jar flowtracker.jar jvmoptsका output भी command line में जोड़ें - Default रूप से FlowTracker port 8011 पर web server start करता है, इसलिए browser में
http://localhost:8011/खोलें
- Github releases pages से
- अधिक detailed configuration options
USAGE.mdमें हैं
अंदरूनी कामकाज: bytecode instrumentation और Tracker model
- FlowTracker एक instrumentation agent है जो JVM द्वारा classes load करते समय class files, यानी bytecode में code inject करता है
- Injected code program द्वारा data पढ़ने, pass करने और write करने के दौरान memory में data और उसके origins की mapping बनाए रखता है
- Tracking का focus
String,char,byte[]जैसे text और binary data पर है; numbers, structured data और computed data इसका core focus नहीं हैं - इस्तेमाल होने के तरीके:
- कुछ JDK method calls को FlowTracker version के method calls से replace करता है
- Input और output track करने के लिए JDK के core locations में code inject करता है
- Method के अंदर local variables और stack values track करने के लिए data flow analysis और deeper instrumentation करता है
- Method call से पहले/बाद और called method के start/end पर code add करके
ThreadLocalके जरिए arguments और return values track करता है
-
Tracker data model
Tracker: tracked object का content और origin information store करता हैcontent:InputStreamयाOutputStreamसे गुजरे सभी bytes जैसा datasource: content की किसी specific range को किसी दूसरे tracker की specific range से connect करता हैTrackerRepository: interesting objects और उनके संबंधितTrackerको connect करने वाला बड़ा globalMap<Object, Tracker>रखता हैTrackerPoint: tracker के अंदर किसी एक position को point करता है और एक single primitive value, जैसे किसी एकbyteकी origin, को represent करता है
बेसिक instrumentation: JDK hooks और ASM
- FlowTracker कुछ खास JDK methods के call होने पर hook method calls insert करके
Trackerको up-to-date रखता है - सबसे सरल उदाहरण
System.arraycopyहैjava.lang.System.arraycopycall कोcom.coekie.flowtracker.hook.SystemHook.arraycopycall से replace करता हैSystemHookअसलीarraycopycall करने के बाद,TrackerRepositoryसे source और target arrays के trackers लाता है और target tracker को source की ओर point करने के लिए update करता है
- इस तरह की instrumentation के लिए ASM bytecode manipulation library का उपयोग किया जाता है
- अधिकतर hooks caller side पर नहीं, बल्कि JDK method के अंदर callee side पर जोड़े जाते हैं
- उदाहरण के लिए
FileInputStream.read(byte[])के अंत मेंFileInputStreamHook.afterReadByteArraycall जोड़ा जाता है - यह instrumentation ASM के
AdviceAdapterका उपयोग करने वाले annotation-based custom micro framework से implement की गई है
- उदाहरण के लिए
- FlowTracker JDK की I/O-related classes जैसे
java.io.FileInputStream,java.io.FileOutputStream,sun.nio.ch.FileChannelImpl,sun.nio.ch.IOUtil,sun.nio.ch.NioSocketImplमें hooks जोड़ता है - संबंधित implementation:
primitive values की tracking और method के अंदर data flow analysis
byteजैसे primitive values की objects जैसी identity नहीं होती, इसलिए उन्हेंTrackerRepositoryकेMapkey के रूप में सुरक्षित तरीके से track नहीं किया जा सकता- FlowTracker code को फिर से लिखता है ताकि primitive values के source को method के अंदर local variables में अलग से store किया जा सके
- उदाहरण के लिए
byte b = x[1]के बादArrayHook.getElementTracker(x, 1)सेbका tracker मिलता है, औरy[2] = bके समयArrayHook.setElementTracker(y, 2, bTracker)से target array में source record किया जाता है - इसके लिए FlowTracker ASM की analysis capabilities के ऊपर symbolic interpretation करता है
- method के हर point पर local variables और stack की values कहां से आईं और कहां जाती हैं, इसे model करता है
- संबंधित implementation:
- FlowValue और ArrayLoadValue
- MergedValue: if statement या loop जैसे control flow के कारण value कई positions से आ सकती हो, ऐसी स्थितियों को handle करता है
- FlowInterpreter: ASM
Interpreterextension के रूप में bytecode instructions को interpret करता है और उपयुक्तFlowValueबनाता है - Store और ArrayStore
- FlowTransformer: पूरी analysis और instrumentation प्रक्रिया को drive करता है
- यह सभी primitive values को track नहीं करता; focus
byteऔरcharपर है, औरintवlongको उनसे ज्यादा limited तरीके से handle किया जाता है
method calls के पार data flow
- केवल method-internal analysis से उन स्थितियों को handle नहीं किया जा सकता जहां primitive values दूसरे methods के arguments और return values के रूप में flow करती हैं
- FlowTracker
Invocationमें arguments और return values केPointTrackerstore करता है, और method call से ठीक पहले इसेThreadLocalमें डालता है - called method के start point पर
Invocation.start(...)सेThreadLocalकी जानकारी निकालकर primitive arguments के sources का उपयोग किया जा सकता है - इस तरीके से
out.write(b)की तरह primitive value को method में pass करने पर भीwrite(byte value)के अंदरvalueका tracker आगे carry किया जा सकता है - संबंधित implementation:
कोड को ही डेटा स्रोत की तरह扱ना
- FlowTracker जिन मुख्य स्रोतों को ट्रैक करता है, वे I/O और कोड से आने वाली values हैं
- कोड से आने वाली values में
'a',"abc"जैसे primitive औरStringconstants शामिल हैं - ऐसे constants के लिए हर class के हिसाब से
ClassOriginTrackerबनाया जाता है, और class व constant references को text के रूप में represent करने वाली सामग्री रखी जाती है - जब कोई constant reference होता है, तो उस value का tracker इस text representation के भीतर की position की ओर इशारा करता है
- यह model constants को ऐसे treat करता है जैसे वे code के text representation से पढ़े गए हों, इसलिए यह I/O tracking model जैसा हो जाता है
- performance की वजह से,
constantPointmethod हर method execution पर call न हो, इसके लिए ConstantDynamic (JEP 309) का इस्तेमाल किया जाता है - संबंधित implementation:
String literal की handling और सीमाएं
- String literal की एक नई
Stringcopy बनाई जाती है, औरString.valueके भीतर केbyte[]कोClassOriginTrackerसे जोड़ा जाता है String s = "abc";जैसा statementString s = StringHook.constantString("abc", 1234, 81);के रूप में rewrite किया जाता है- यह तरीका JVM द्वारा आम तौर पर दी जाने वाली String interning guarantee को तोड़ देता है
- मूल रूप से, एक ही String constant की सभी occurrences को उसी instance को reference करना चाहिए
- instrumentation के बाद, इस guarantee पर निर्भर code टूट सकता है
- FlowTracker इस समस्या को कम करने के लिए कुछ उपाय रखता है
- ConstantDynamic का इस्तेमाल करके, एक ही line का वही String literal कई बार execute होने पर भी हर बार वही instance return करता है
- कुछ
stringA == stringBexpressions कोObjects.equals(stringA, stringB)के रूप में rewrite करता है, ताकि कुछ नजरियों से वे same instance जैसे दिखें java.lang.*जैसे कुछ packages में String literal tracking disable कर देता है- यह behavior
USAGE.mdकेbreakStringInterningसे configure किया जा सकता है
- संबंधित implementation:
जिन values को track नहीं किया जाता, उनके लिए fallback
- FlowTracker program की सभी values को track नहीं करता
- वजहें हैं performance concerns, वे हिस्से जो अभी implement नहीं हुए हैं, कम relevance वाली values, और यह कि कई sources के combination से बनने वाली values को represent करने के लिए अधिक जटिल data model चाहिए
- जब कोई ऐसी value, जो अब तक track नहीं हो रही थी, ऐसी जगह पहुंचती है जहां tracking शुरू होनी चाहिए, तो उसे constants की तरह
ClassOriginTrackerसे जोड़ा जाता है और उसकी position को"<?>"के रूप में represent किया जाता है - उदाहरण के लिए, array length track नहीं होती, इसलिए जब
write(array.length)call होता है, तोInvocationकोwritecall site की code position की ओर इशारा करने वालाPointTrackerदिया जाता है - नतीजतन, binary format के output में भले ही original source न दिखे, आसपास की tracked strings और code position के जरिए कभी-कभी value का अर्थ जल्दी समझा जा सकता है
और गहराई में देखे जा सकने वाले implementation विषय
- MergedValue branches और loops से गुजरने वाली values की tracking को संभालता है, जिसे data flow analysis के सबसे कठिन हिस्सों में माना जाता है
- String concatenation को indification (JEP 280) के जरिए
StringConcatFactoryद्वारा लौटाए गएMethodHandleमें hook जोड़कर handle किया जाता है - implementation में source code ढूंढना, Vineflower से decompile करना, और bytecode को source lines से जोड़ना भी शामिल है
- ClassLoader configuration का focus bootclasspath dependencies और application conflicts से बचने, और shading व nested jar के बिना तेज़ development cycle बनाए रखने पर है
- fields में stored primitive values की tracking भी implementation में शामिल है
- frontend Jetty और JAX-RS आधारित web server, और Svelte आधारित web UI से बना है
1 टिप्पणियां
Hacker News की रायें
instrumentation के लिए instrumentation agent के बजाय आधिकारिक Clojure compiler का fork इस्तेमाल करता है, और Clojure की उस खूबी का फायदा उठाता है जिससे development के दौरान compiler को आसानी से बदला जा सकता है, ताकि अतिरिक्त bytecode डाला जा सके
Clojure program execution record में दिलचस्प बात यह है कि ज्यादातर values immutable होती हैं, इसलिए सिर्फ pointers बनाए रखकर snapshots लिए जा सकते हैं
मूल पोस्ट का demo web app exploration है, इसलिए रुचि रखने वालों के लिए FlowStorm से web app debugging का demo भी छोड़ रहा हूं https://www.youtube.com/watch?v=h8AFpZkAwPo
Java/JVM ecosystem के tools इतने अच्छे हैं, यह अच्छी बात है। इस तरह मैं पहले jitwatch देखकर हैरान हुआ था https://github.com/AdoptOpenJDK/jitwatch
FlowTracker से थोड़ा taint analysis याद आता है, जो untrusted user input या secrets program के अंदर कैसे flow करते हैं, यह track करता है ताकि वे leak न हों या validation के बिना इस्तेमाल न हों
search keyword “dynamic taint tracking/analysis” है
https://github.com/gmu-swe/phosphor
https://github.com/soot-oss/SootUp
https://github.com/feliam/klee-taint
आगे चलकर ऐसे tools bugs track करते समय first line of defense बनें, यह आसानी से कल्पना की जा सकती है
FlowTracker develop करते हुए काफी काम किसी खास example program की tracking को काम कराने से निकला
target result पता था, लेकिन किसी खास example के काम करने के लिए कौन-से low-level mechanisms support करने होंगे, यह predict करना मुश्किल था, और यह अक्सर उन JDK या library internals की implementation details पर निर्भर करता था जिनसे data गुजरता था
लेकिन HTML element का उस SQL script से जुड़ना, जिसने वह data DB में डाला था, वैसा नहीं था
यह उम्मीद या इरादे से बनाया गया नहीं था, बस ऐसा हो गया, इसलिए मैं भी काफी हैरान हुआ और इस approach से और क्या किया जा सकता है, इसे लेकर उत्साहित हुआ
यह track करने का तरीका भी अच्छा होगा कि data temporary है या उसे फिर से लिखा जाना चाहिए
ऐसी constraints को जितना ज्यादा upfront describe कर सकें, उतना बेहतर
Smalltalk में सब कुछ objects और messages है, इसलिए backtrack कर सकते हैं और interact कर सकते हैं
इसे और expand करने का समय नहीं निकाल पाया, लेकिन web development tools को इस तरह की full-stack attribution tracking से बड़ा फायदा मिल सकता है
हालांकि ऐसे solution को existing frameworks में integrate करना बड़ी challenge लगता है
[1] HTML Source Maps - https://github.com/connorjclark/html-source-maps https://docs.google.com/document/d/19XYWiPL9h9vA6QcOrGV9Nfkr...
https://www.youtube.com/watch?v=TWAMr72VaaU&t=164s और https://witheve.com/
FlowTracker जो करता है उसे extend करें तो SQL या अन्य injection vulnerabilities भी खोजी जा सकती हैं। इसलिए संभव है कि आपके याद किए tool ने similar approach इस्तेमाल किया हो
या “यह string बनने के क्षण से मेरी screen तक पहुंचने तक क्या-क्या देख चुकी है” जैसे सवाल
यह उस दिशा में एक कदम लगता है
अभी थोड़ी देर के लिए रोकना होगा, लेकिन इसे चलाकर इससे छेड़छाड़ करने का इंतजार है